package com.xbongbong.admin.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.google.common.collect.Maps;
import com.xbongbong.admin.domain.entity.AppInstallEntity;
import com.xbongbong.admin.domain.entity.BusinessRuleInfoRetainEntity;
import com.xbongbong.admin.domain.entity.ChartBindAppInfoEntity;
import com.xbongbong.admin.domain.entity.FormBindTemplateInfoEntity;
import com.xbongbong.admin.domain.entity.FormInstallEntity;
import com.xbongbong.admin.domain.entity.LowCodeRetaionEntity;
import com.xbongbong.admin.domain.entity.MarketDetailEntity;
import com.xbongbong.admin.domain.entity.MirrorAppEntity;
import com.xbongbong.admin.domain.entity.MirrorBusinessRuleInfoEntity;
import com.xbongbong.admin.domain.entity.MirrorChartCategoryEntity;
import com.xbongbong.admin.domain.entity.MirrorChartEntity;
import com.xbongbong.admin.domain.entity.MirrorConfigEntity;
import com.xbongbong.admin.domain.entity.MirrorFormEntity;
import com.xbongbong.admin.domain.entity.MirrorFormExplainEntity;
import com.xbongbong.admin.domain.entity.MirrorLabelEntity;
import com.xbongbong.admin.domain.entity.MirrorLabelGroupEntity;
import com.xbongbong.admin.domain.entity.MirrorLowCodeEntity;
import com.xbongbong.admin.domain.entity.MirrorMenuEntity;
import com.xbongbong.admin.domain.entity.MirrorPrintTemplateEntity;
import com.xbongbong.admin.domain.entity.MirrorRetainLabelEntity;
import com.xbongbong.admin.domain.entity.MirrorRetainLabelGroupEntity;
import com.xbongbong.admin.domain.entity.MirrorStageEntity;
import com.xbongbong.admin.domain.entity.MirrorStageProcessEntity;
import com.xbongbong.admin.domain.entity.MirrorStageWorkEntity;
import com.xbongbong.admin.domain.entity.MirrorWorkOrderTemplateCategoryEntity;
import com.xbongbong.admin.domain.entity.MirrorWorkOrderTemplateEntity;
import com.xbongbong.admin.domain.entity.PaasAppRetainEntity;
import com.xbongbong.admin.domain.entity.PaasFormExplainRetainEntity;
import com.xbongbong.admin.domain.entity.PaasFormRetainEnity;
import com.xbongbong.admin.domain.entity.PaasMenuRetainEntity;
import com.xbongbong.admin.domain.entity.PaasPrintTemplateRetainEntity;
import com.xbongbong.admin.domain.entity.RaidersEntity;
import com.xbongbong.admin.domain.entity.WorkOrderStageRetainEntity;
import com.xbongbong.admin.domain.entity.WorkOrderTemplateCategoryRetainEntity;
import com.xbongbong.admin.domain.entity.WorkOrderTemplateRetainEntity;
import com.xbongbong.admin.feign.ChartInstallFeignClient;
import com.xbongbong.admin.feign.FormBusinessRuleFeignClient;
import com.xbongbong.admin.feign.WorkOrderV2FeignClient;
import com.xbongbong.admin.model.AppInstallModel;
import com.xbongbong.admin.model.BusinessRuleInfoRetainModel;
import com.xbongbong.admin.model.ChartBindAppInfoModel;
import com.xbongbong.admin.model.FormBindTemplateInfoModel;
import com.xbongbong.admin.model.FormInstallModel;
import com.xbongbong.admin.model.LowCodeRetaionModel;
import com.xbongbong.admin.model.MirrorAppModel;
import com.xbongbong.admin.model.MirrorBusinessRuleInfoModel;
import com.xbongbong.admin.model.MirrorChartCategoryModel;
import com.xbongbong.admin.model.MirrorChartModel;
import com.xbongbong.admin.model.MirrorConfigModel;
import com.xbongbong.admin.model.MirrorFormExplainModel;
import com.xbongbong.admin.model.MirrorFormModel;
import com.xbongbong.admin.model.MirrorLabelGroupModel;
import com.xbongbong.admin.model.MirrorLabelModel;
import com.xbongbong.admin.model.MirrorLowCodeModel;
import com.xbongbong.admin.model.MirrorMenuModel;
import com.xbongbong.admin.model.MirrorPrintTemplateModel;
import com.xbongbong.admin.model.MirrorRetainLabelGroupModel;
import com.xbongbong.admin.model.MirrorRetainLabelModel;
import com.xbongbong.admin.model.MirrorStageModel;
import com.xbongbong.admin.model.MirrorStageProcessModel;
import com.xbongbong.admin.model.MirrorStageWorkModel;
import com.xbongbong.admin.model.MirrorWorkOrderTemplateCategoryModel;
import com.xbongbong.admin.model.MirrorWorkOrderTemplateModel;
import com.xbongbong.admin.model.PaasAppRetainModel;
import com.xbongbong.admin.model.PaasFormExplainRetainModel;
import com.xbongbong.admin.model.PaasFormRetainModel;
import com.xbongbong.admin.model.PaasMenuRetainModel;
import com.xbongbong.admin.model.PaasPrintTemplateRetainModel;
import com.xbongbong.admin.model.RaidersModel;
import com.xbongbong.admin.model.WorkOrderStageRetainModel;
import com.xbongbong.admin.model.WorkOrderTemplateCategoryRetainModel;
import com.xbongbong.admin.model.WorkOrderTemplateRetainModel;
import com.xbongbong.admin.pojo.LowCodeConfigRetaionPojo;
import com.xbongbong.admin.pojo.MirrorConfigListPojo;
import com.xbongbong.admin.pojo.StageInstallPojo;
import com.xbongbong.admin.pojo.StageRetainPojo;
import com.xbongbong.admin.pojo.StageRollbackPojo;
import com.xbongbong.admin.pojo.dto.MirrorChartPreviewDTO;
import com.xbongbong.admin.pojo.dto.MirrorInstallAllDTO;
import com.xbongbong.admin.pojo.dto.MirrorInstallDTO;
import com.xbongbong.admin.pojo.dto.MirrorInstructionAllDTO;
import com.xbongbong.admin.pojo.dto.MirrorInstructionDTO;
import com.xbongbong.admin.pojo.dto.MirrorMenuListAllDTO;
import com.xbongbong.admin.pojo.dto.MirrorMenuListDTO;
import com.xbongbong.admin.pojo.dto.MirrorPreviewDTO;
import com.xbongbong.admin.pojo.dto.MirrorRollBackDTO;
import com.xbongbong.admin.pojo.vo.ChartMenuVO;
import com.xbongbong.admin.pojo.vo.MirrorChartDataVO;
import com.xbongbong.admin.pojo.vo.MirrorChartListAllVO;
import com.xbongbong.admin.pojo.vo.MirrorChartListVO;
import com.xbongbong.admin.pojo.vo.MirrorChartPojoVO;
import com.xbongbong.admin.pojo.vo.MirrorChartPreviewVO;
import com.xbongbong.admin.pojo.vo.MirrorConfigListVo;
import com.xbongbong.admin.pojo.vo.MirrorConfigVO;
import com.xbongbong.admin.pojo.vo.MirrorFormVO;
import com.xbongbong.admin.pojo.vo.MirrorInstallAllVO;
import com.xbongbong.admin.pojo.vo.MirrorInstallVO;
import com.xbongbong.admin.pojo.vo.MirrorInstructionAllVO;
import com.xbongbong.admin.pojo.vo.MirrorInstructionVO;
import com.xbongbong.admin.pojo.vo.MirrorMenuListVO;
import com.xbongbong.admin.pojo.vo.MirrorMenuVO;
import com.xbongbong.admin.pojo.vo.MirrorPreviewVO;
import com.xbongbong.admin.pojo.vo.MirrorResultChartVO;
import com.xbongbong.admin.pojo.vo.MirrorRollBackVO;
import com.xbongbong.admin.pojo.vo.MirrorSystemMenuListVO;
import com.xbongbong.admin.pojo.vo.MirrorSystemMenuVO;
import com.xbongbong.admin.pojo.vo.MirrorUpdateVO;
import com.xbongbong.admin.pojo.vo.MirrorWorkOrderListVO;
import com.xbongbong.admin.pojo.vo.WorkOrderCategoryVO;
import com.xbongbong.admin.pojo.vo.WorkOrderTemplateVO;
import com.xbongbong.admin.service.MirrorService;
import com.xbongbong.base.BaseIdListDTO;
import com.xbongbong.i18n.util.I18nMessageUtil;
import com.xbongbong.paas.constant.MessageConstant;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.domain.dao.PaasPrintTemplateDao;
import com.xbongbong.paas.domain.entity.BusinessRuleInfoEntity;
import com.xbongbong.paas.domain.entity.PaasAppEntity;
import com.xbongbong.paas.domain.entity.PaasFormEntity;
import com.xbongbong.paas.domain.entity.PaasFormExplainEntity;
import com.xbongbong.paas.domain.entity.PaasMenuEntity;
import com.xbongbong.paas.domain.entity.PaasPrintTemplateEntity;
import com.xbongbong.paas.domain.entity.PaasStageEntity;
import com.xbongbong.paas.domain.entity.StageLogEntity;
import com.xbongbong.paas.domain.entity.StageProcessEntity;
import com.xbongbong.paas.domain.entity.StageTimeoutRemindEntity;
import com.xbongbong.paas.domain.entity.StageWorkLogEntity;
import com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt;
import com.xbongbong.paas.domain.entity.ext.PaasFormEntityExt;
import com.xbongbong.paas.elasticsearch.util.EsUtil;
import com.xbongbong.paas.elasticsearch.util.XbbElasticsearchRestTemplate;
import com.xbongbong.paas.enums.AddressEnum;
import com.xbongbong.paas.enums.BIProductReformEnum;
import com.xbongbong.paas.enums.ComboTypeEnum;
import com.xbongbong.paas.enums.DateTimeEnum;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.enums.FieldTypeEnum;
import com.xbongbong.paas.enums.IndexTypeEnum;
import com.xbongbong.paas.enums.MenuTypeEnum;
import com.xbongbong.paas.enums.OptionalRangeEnum;
import com.xbongbong.paas.enums.PatternEnum;
import com.xbongbong.paas.enums.PlatFormEnum;
import com.xbongbong.paas.enums.SeparatorEnum;
import com.xbongbong.paas.enums.VisibleTypeEnum;
import com.xbongbong.paas.field.FieldAttrEntity;
import com.xbongbong.paas.field.FilterConditionsPoJo;
import com.xbongbong.paas.field.FormulaPoJo;
import com.xbongbong.paas.field.ItemPoJo;
import com.xbongbong.paas.field.LinkFormPoJo;
import com.xbongbong.paas.field.LinkInfoPojo;
import com.xbongbong.paas.field.LinkKeyFieldInfoPojo;
import com.xbongbong.paas.field.LinkedAttrPoJo;
import com.xbongbong.paas.field.RelyPoJo;
import com.xbongbong.paas.field.SubFormPoJo;
import com.xbongbong.paas.field.VisibleRulePoJo;
import com.xbongbong.paas.help.LowCodeHelp;
import com.xbongbong.sys.help.PackageHelp;
import com.xbongbong.paas.model.BusinessRuleInfoModel;
import com.xbongbong.paas.model.CustomButtonActionModel;
import com.xbongbong.paas.model.CustomButtonModel;
import com.xbongbong.paas.model.LowCodeActionSettingModel;
import com.xbongbong.paas.model.LowCodeJsFileModel;
import com.xbongbong.paas.model.PaasAppModel;
import com.xbongbong.paas.model.PaasFormExplainModel;
import com.xbongbong.paas.model.PaasFormModel;
import com.xbongbong.paas.model.PaasMenuModel;
import com.xbongbong.paas.model.PaasProcessTaskModel;
import com.xbongbong.paas.model.StageProcessModel;
import com.xbongbong.paas.model.StageTimeoutRemindModel;
import com.xbongbong.paas.model.StageWorkLogModel;
import com.xbongbong.paas.pojo.OperationsPojo;
import com.xbongbong.paas.pojo.dto.BusinessRuleWhiteDTO;
import com.xbongbong.paas.pojo.vo.BusinessRuleWhiteVO;
import com.xbongbong.paas.redis.PaasRedisHelper;
import com.xbongbong.paas.redis.RedisConstant;
import com.xbongbong.paas.redis.RedisPrefixConstant;
import com.xbongbong.paas.service.stage.StageHandle;
import com.xbongbong.paas.service.stage.StageHandleFactory;
import com.xbongbong.paas.service.stage.StageLogHandle;
import com.xbongbong.paas.service.stage.StageLogHandleFactory;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.helper.PageHelper;
import com.xbongbong.paas.toolbox.util.BeanUtil;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.paas.toolbox.util.DateTimeUtil;
import com.xbongbong.paas.toolbox.util.DateUtil;
import com.xbongbong.paas.toolbox.util.JsonHelperUtil;
import com.xbongbong.paas.toolbox.util.RegexUtil;
import com.xbongbong.paas.toolbox.util.StringUtil;
import com.xbongbong.paas.toolbox.wrap.UserVO;
import com.xbongbong.paas.toolbox.wrap.XbbResponse;
import com.xbongbong.paas.util.ExplainUtil;
import com.xbongbong.pro.actionsetting.pojo.LowCodeActionSettingPojo;
import com.xbongbong.pro.appmodule.pojo.AppChartPojo;
import com.xbongbong.pro.config.ProBaseConfig;
import com.xbongbong.pro.constant.BusinessConstant;
import com.xbongbong.pro.constant.CommonConstant;
import com.xbongbong.pro.constant.ElasticConstant;
import com.xbongbong.pro.custombutton.pojo.CustomButtonActionPojo;
import com.xbongbong.pro.custombutton.pojo.CustomButtonPojo;
import com.xbongbong.pro.domain.entity.AppModuleEntity;
import com.xbongbong.pro.domain.entity.ChartCategoryEntity;
import com.xbongbong.pro.domain.entity.ChartEntity;
import com.xbongbong.pro.domain.entity.CustomButtonActionEntity;
import com.xbongbong.pro.domain.entity.CustomButtonEntity;
import com.xbongbong.pro.domain.entity.LabelEntity;
import com.xbongbong.pro.domain.entity.LabelGroupEntity;
import com.xbongbong.pro.domain.entity.LowCodeActionSettingEntity;
import com.xbongbong.pro.domain.entity.LowCodeJsFileEntity;
import com.xbongbong.pro.domain.entity.StageProcessRetainEntity;
import com.xbongbong.pro.domain.entity.StageRetainEntity;
import com.xbongbong.pro.domain.entity.StageWorkRetainEntity;
import com.xbongbong.pro.enums.DistributorMarkEnum;
import com.xbongbong.pro.enums.ErrorCodeEnum;
import com.xbongbong.pro.enums.PackageStatusEnum;
import com.xbongbong.pro.enums.SaasMarkEnum;
import com.xbongbong.pro.enums.ValueAddedTypeEnum;
import com.xbongbong.pro.enums.businessrule.enums.RuleVersionEnum;
import com.xbongbong.pro.enums.errorcodes.MirrorErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.StatisticsServicesErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.SystemLabelErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.TemplateCenterErrorCodeEnum;
import com.xbongbong.pro.enums.menu.enums.FundManagementMenuEnum;
import com.xbongbong.pro.enums.menu.enums.JXCMenuEnum;
import com.xbongbong.pro.enums.menu.enums.ProductMenuEnum;
import com.xbongbong.pro.enums.menu.enums.SystemAppMenuEnum;
import com.xbongbong.pro.enums.mirror.enums.AppInstallTypeEnum;
import com.xbongbong.pro.enums.mirror.enums.FormBindTemplateTypeEnum;
import com.xbongbong.pro.enums.mirror.enums.MirrorApplicationTypeEnum;
import com.xbongbong.pro.enums.mirror.enums.MirrorTypeEnum;
import com.xbongbong.pro.lowcodejsfile.pojo.JsFilePojo;
import com.xbongbong.pro.script.pojo.dto.MirrorUpdateDTO;
import com.xbongbong.pro.service.aliyun.AliyunOssService;
import com.xbongbong.pro.service.toolbox.statistic.help.FormHelp;
import com.xbongbong.pro.statistic.enums.ChartTypeEnum;
import com.xbongbong.pro.statistic.model.ChartCategoryModel;
import com.xbongbong.pro.statistic.pojo.SlaveSourcePojo;
import com.xbongbong.pro.statistic.pojo.dto.ChartMirrorInstallDTO;
import com.xbongbong.pro.statistic.pojo.vo.ChartMirrorInstallVO;
import com.xbongbong.pro.statistic.result.pojo.ChartStatisticsPojo;
import com.xbongbong.pro.statistic.result.pojo.FieldAttrPojo;
import com.xbongbong.pro.statistic.result.pojo.vo.ResultDataChartVO;
import com.xbongbong.pro.statistic.result.pojo.vo.ResultDataSeriesChartVO;
import com.xbongbong.pro.statistic.result.pojo.vo.ResultTableVO;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.ParameterConstant;
import com.xbongbong.saas.constant.StringConstant;
import com.xbongbong.saas.domain.entity.SaleStageModifyLogEntity;
import com.xbongbong.saas.domain.entity.SaleStageSyncStatusEntity;
import com.xbongbong.saas.domain.entity.StageWorkEntity;
import com.xbongbong.saas.domain.entity.WorkOrderExplainEntity;
import com.xbongbong.saas.domain.entity.WorkOrderFormEntity;
import com.xbongbong.saas.domain.entity.WorkOrderStageEntity;
import com.xbongbong.saas.domain.entity.WorkOrderTemplateCategoryEntity;
import com.xbongbong.saas.domain.entity.WorkOrderTemplateEntity;
import com.xbongbong.saas.domain.entity.ext.WorkOrderFormEntityExt;
import com.xbongbong.saas.enums.CompanyConfigEnum;
import com.xbongbong.saas.enums.MirrorRollBackTypeEnum;
import com.xbongbong.saas.enums.OperateModuleTypeEnum;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.model.AppModuleModel;
import com.xbongbong.saas.model.ClueWorkLogModel;
import com.xbongbong.saas.model.CustomerWorkLogModel;
import com.xbongbong.saas.model.IndexTypeModel;
import com.xbongbong.saas.model.LabelGroupModel;
import com.xbongbong.saas.model.LabelModel;
import com.xbongbong.saas.model.MarketDetailModel;
import com.xbongbong.saas.model.OpportunityWorkLogModel;
import com.xbongbong.saas.model.SaleStageModifyLogModel;
import com.xbongbong.saas.model.SaleStageSyncStatusModel;
import com.xbongbong.saas.model.StageProcessRetainModel;
import com.xbongbong.saas.model.StageRetainModel;
import com.xbongbong.saas.model.StageWorkModel;
import com.xbongbong.saas.model.StageWorkRetainModel;
import com.xbongbong.saas.model.WorkOrderExplainModel;
import com.xbongbong.saas.model.WorkOrderFormModel;
import com.xbongbong.saas.model.WorkOrderModel;
import com.xbongbong.saas.model.WorkOrderStageModel;
import com.xbongbong.saas.model.WorkOrderTemplateCategoryModel;
import com.xbongbong.saas.model.WorkOrderTemplateModel;
import com.xbongbong.saas.service.toolbox.help.PackageStatusHelp;
import com.xbongbong.sys.domain.entity.CompanyConfigEntity;
import com.xbongbong.sys.model.CompanyConfigModel;
import com.xbongbong.sys.model.FeeLogModel;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Random;
import java.util.Set;
import java.util.stream.Collectors;

import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
import static org.elasticsearch.index.query.QueryBuilders.termsQuery;

/**
 * 镜像功能
 * @author xingxing.xiao
 */
@Service("mirrorService")
public class MirrorServiceImpl implements MirrorService {
    private static final Logger LOG = LoggerFactory.getLogger(MirrorServiceImpl.class);

    private static Random random = new Random();


    @Resource
    private MirrorConfigModel mirrorConfigModel;
    @Resource
    private MirrorAppModel mirrorAppModel;
    @Resource
    private MirrorMenuModel mirrorMenuModel;
    @Resource
    private MirrorFormModel mirrorFormModel;
    @Resource
    private MirrorFormExplainModel mirrorFormExplainModel;
    @Resource
    private MirrorStageProcessModel mirrorStageProcessModel;
    @Resource
    private MirrorStageModel mirrorStageModel;
    @Resource
    private MirrorStageWorkModel mirrorStageWorkModel;
    @Resource
    private MirrorPrintTemplateModel mirrorPrintTemplateModel;
    @Resource
    private MirrorBusinessRuleInfoModel mirrorBusinessRuleInfoModel;
    @Resource
    private BusinessRuleInfoModel businessRuleInfoModel;
    @Resource
    private BusinessRuleInfoRetainModel businessRuleInfoRetainModel;
    @Resource
    private PaasAppModel paasAppModel;
    @Resource
    private PaasMenuModel paasMenuModel;
    @Resource
    private PaasFormModel paasFormModel;
    @Resource
    private PaasFormExplainModel paasFormExplainModel;
    @Resource
    private StageProcessModel stageProcessModel;
    @Resource
    private StageHandleFactory stageHandleFactory;
    @Resource
    private StageWorkModel stageWorkModel;
    @Resource
    private StageLogHandleFactory stageLogHandleFactory;
    @Resource
    private StageWorkLogModel stageWorkLogModel;
    @Resource
    private CustomerWorkLogModel customerWorkLogModel;
    @Resource
    private OpportunityWorkLogModel opportunityWorkLogModel;
    @Resource
    private ClueWorkLogModel clueWorkLogModel;
    @Resource
    private SaleStageModifyLogModel saleStageModifyLogModel;
    @Resource
    private SaleStageSyncStatusModel saleStageSyncStatusModel;
    @Resource
    private StageTimeoutRemindModel stageTimeoutRemindModel;
    @Resource
    private PaasPrintTemplateDao paasPrintTemplateDao;
    @Resource
    private AppInstallModel appInstallModel;
    @Resource
    private WorkOrderFormModel workOrderFormModel;
    @Resource
    private RaidersModel raidersModel;
    @Resource
    private WorkOrderTemplateCategoryModel workOrderTemplateCategoryModel;
    @Resource
    private WorkOrderTemplateModel workOrderTemplateModel;
    @Resource
    private WorkOrderExplainModel workOrderExplainModel;
    @Resource
    private MirrorWorkOrderTemplateCategoryModel mirrorWorkOrderTemplateCategoryModel;
    @Resource
    private MirrorWorkOrderTemplateModel mirrorWorkOrderTemplateModel;
    @Resource
    private FormBindTemplateInfoModel formBindTemplateInfoModel;
    @Resource
    private ChartBindAppInfoModel chartBindAppInfoModel;
    @Resource
    private MirrorChartModel mirrorChartModel;
    @Resource
    private MirrorChartCategoryModel mirrorChartCategoryModel;
    @Resource
    private ChartCategoryModel chartCategoryModel;
    @Resource
    private PaasProcessTaskModel paasProcessTaskModel;
    @Resource
    private WorkOrderModel workOrderModel;
    @Resource
    private WorkOrderStageModel workOrderStageModel;
    @Resource
    private FormInstallModel formInstallModel;
    @Resource
    private PaasAppRetainModel paasAppRetainModel;
    @Resource
    private PaasFormExplainRetainModel paasFormExplainRetainModel;
    @Resource
    private PaasFormRetainModel paasFormRetainModel;
    @Resource
    private PaasMenuRetainModel paasMenuRetainModel;
    @Resource
    private StageProcessRetainModel stageProcessRetainModel;
    @Resource
    private StageRetainModel stageRetainModel;
    @Resource
    private StageWorkRetainModel stageWorkRetainModel;
    @Resource
    private PaasPrintTemplateRetainModel paasPrintTemplateRetainModel;
    @Resource
    private WorkOrderTemplateCategoryRetainModel workOrderTemplateCategoryRetainModel;
    @Resource
    private WorkOrderTemplateRetainModel workOrderTemplateRetainModel;
    @Resource
    private WorkOrderStageRetainModel workOrderStageRetainModel;
    @Resource
    private PackageStatusHelp  packageStatusHelp;
    @Resource
    private PaasRedisHelper paasRedisHelper;
    @Resource
    private FormBusinessRuleFeignClient formBusinessRuleFeignClient;
    @Resource
    private CompanyConfigModel companyConfigModel;
    @Resource
    private ChartInstallFeignClient chartInstallFeignClient;
    @Resource
    private XbbElasticsearchRestTemplate xbbElasticsearchRestTemplate;
    @Resource
    private MirrorLabelModel mirrorLabelModel;
    @Resource
    private MirrorLabelGroupModel mirrorLabelGroupModel;
    @Resource
    private LabelGroupModel labelGroupModel;
    @Resource
    private LabelModel labelModel;
    @Resource
    private MirrorRetainLabelModel mirrorRetainLabelModel;
    @Resource
    private MirrorRetainLabelGroupModel mirrorRetainLabelGroupModel;
    @Resource
    private AppModuleModel appModuleModel;
    @Resource
    private MarketDetailModel marketDetailModel;
    @Resource
    private PackageHelp packageHelp;
    @Resource
    private FormHelp formHelp;
    @Resource
    private IndexTypeModel indexTypeModel;
    @Resource
    private LowCodeJsFileModel lowCodeJsFileModel;
    @Resource
    private CustomButtonModel customButtonModel;
    @Resource
    private CustomButtonActionModel customButtonActionModel;
    @Resource
    private LowCodeActionSettingModel lowCodeActionSettingModel;
    @Resource
    private ProBaseConfig proBaseConfig;
    @Resource
    private AliyunOssService aliyunOssService;
    @Resource
    private LowCodeRetaionModel lowCodeRetaionModel;
    @Resource
    private MirrorLowCodeModel mirrorLowCodeModel;
    @Resource
    private LowCodeHelp lowCodeHelp;

    @Resource
    private WorkOrderV2FeignClient workOrderV2FeignClient;

    @Resource
    private FeeLogModel feeLogModel;


    /**
     * 镜像安装
     * @param mirrorInstallDTO
     * @return
     * @throws XbbException
     * @author xingxing.xiao
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public MirrorInstallVO install(MirrorInstallDTO mirrorInstallDTO) throws XbbException {
        MirrorInstallVO mirrorInstallVO = new MirrorInstallVO();
        try {
            // 把所有东西再复制回去
            Long mirrorId = mirrorInstallDTO.getMirrorId();
            String corpid = mirrorInstallDTO.getCorpid();
            String creatorId = mirrorInstallDTO.getUserId();
            Boolean module = mirrorInstallDTO.getModule();
            MirrorConfigEntity mirrorConfigEntity = mirrorConfigModel.getByKey(mirrorId);
            if (Objects.isNull(mirrorConfigEntity)) {
                throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403001, MirrorErrorCodeEnum.API_ERROR_403001.getMsg());
            }
            // 该镜像来源公司id
            String sourceCorpid = mirrorConfigEntity.getTargetCorpid();
            if (Objects.equals(mirrorConfigEntity.getType(), MirrorTypeEnum.APPLICATION.getType())) {
                // 应用安装(自定义应用 or 系统应用 or 工单)
                MirrorAppEntity mirrorAppEntity = mirrorAppModel.getByMirrorId(mirrorId);
                if (Objects.equals(mirrorAppEntity.getAlias(), XbbRefTypeEnum.WORK_ORDER.getAlias()) && Objects.equals(mirrorAppEntity.getSaasMark(), SaasMarkEnum.SAAS.getCode())) {
                    // 安装工单镜像
                    String creatorName = mirrorInstallDTO.getLoginUser().getName();
                    AppInstallEntity appInstallEntity = appInstallModel.getByMirrorId(mirrorId, corpid);
                    if (Objects.nonNull(appInstallEntity)) {
                        boolean flag = checkInstall(appInstallEntity);
                        if (flag) {
                            throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403006, MirrorErrorCodeEnum.API_ERROR_403006.getMsg());
                        }
                    }
                    Long appInstallId = installWorkOrderMirror(corpid, mirrorId, sourceCorpid, creatorId, creatorName, mirrorAppEntity);
                    mirrorInstallVO.setId(appInstallId);
                } else if (Objects.equals(mirrorAppEntity.getSaasMark(), SaasMarkEnum.SAAS.getCode())) {
                    // 安装系统表单镜像
                    beforeInstallApplication(mirrorId, corpid, MirrorApplicationTypeEnum.SYSTEM.getType(), module, mirrorInstallVO, mirrorAppEntity.getAlias());
                    Long appInstallId = installSystemMirror(corpid, mirrorId, sourceCorpid, creatorId, mirrorAppEntity, mirrorInstallDTO);
                    mirrorInstallVO.setId(appInstallId);
                } else {
                    // 安装自定应用镜像
                    beforeInstallApplication(mirrorId, corpid, MirrorApplicationTypeEnum.CUSTOM.getType(), module, mirrorInstallVO,mirrorAppEntity.getAlias());
                    Long appInstallId = installCustomeMirror(corpid, mirrorId, sourceCorpid, creatorId, mirrorAppEntity, mirrorConfigEntity.getName(), mirrorInstallDTO);
                    mirrorInstallVO.setId(appInstallId);
                }
            } else if (Objects.equals(mirrorConfigEntity.getType(), MirrorTypeEnum.CHART.getType())) {
                AppInstallEntity appInstallEntity = appInstallModel.getByMirrorId(mirrorId, corpid);
                if (Objects.nonNull(appInstallEntity)) {
                    boolean flag = checkInstall(appInstallEntity);
                    if (flag) {
                        throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403006, MirrorErrorCodeEnum.API_ERROR_403006.getMsg());
                    }
                }
                // 图表安装
                installChartMirror(mirrorInstallDTO, corpid,mirrorId,sourceCorpid,creatorId);
            }
        } catch (XbbException e){
            LOG.error("mirrorService install error", e);
            throw e;
        } catch (Exception e) {
            LOG.error("mirror install error", e);
            throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403004);
        }
        return mirrorInstallVO;
    }

    /**
     * 镜像组合同时安装应用和图表
     *
     * @param mirrorInstallAllDTO
     * @return
     * @throws XbbException
     * @author youli.chen
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public MirrorInstallAllVO installAll(MirrorInstallAllDTO mirrorInstallAllDTO) throws XbbException {
        MirrorInstallAllVO mirrorInstallAllVO = new MirrorInstallAllVO();
        List<MirrorInstallVO> installVOs = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        MirrorInstallVO mirrorInstallVO = new MirrorInstallVO();
        try {
            // 把所有东西再复制回去
            Long appModuleId = mirrorInstallAllDTO.getAppModuleId();
            String corpid = mirrorInstallAllDTO.getCorpid();
            String creatorId = mirrorInstallAllDTO.getUserId();
            Boolean module = mirrorInstallAllDTO.getModule();

            AppModuleEntity entity = null;
            if (appModuleId != null) {
                entity = appModuleModel.getByKey(appModuleId);
                if (entity == null) {
                    throw new XbbException(TemplateCenterErrorCodeEnum.API_ERROR_402005);
                }
            }

            List<Long> mirrIdIn;
            if (entity != null) {
                mirrIdIn = JSONObject.parseArray(entity.getMirrorId().toJSONString(), Long.class);
            } else {
                mirrIdIn = JSONObject.parseArray(mirrorInstallAllDTO.getMirrorIdArr().toJSONString(), Long.class);
            }
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("idIn", mirrIdIn);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorConfigEntity> mirrorConfigList = mirrorConfigModel.findEntitys(param);
            if (mirrorConfigList.isEmpty()) {
                throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403001);
            }

            for(MirrorConfigEntity mirrorConfigEntity : mirrorConfigList){
                Long mirrorId = mirrorConfigEntity.getId();
                MirrorInstallDTO mirrorInstallDTO = new MirrorInstallDTO();
                BeanUtil.copyProperties(mirrorInstallAllDTO, mirrorInstallDTO);
                mirrorInstallDTO.setMirrorId(mirrorId);
                // 该镜像来源公司id
                String sourceCorpid = mirrorConfigEntity.getTargetCorpid();
                if (Objects.equals(mirrorConfigEntity.getType(), MirrorTypeEnum.APPLICATION.getType())) {
                    // 应用安装(自定义应用 or 系统应用 or 工单)
                    MirrorAppEntity mirrorAppEntity = mirrorAppModel.getByMirrorId(mirrorId);
                    if (Objects.equals(mirrorAppEntity.getAlias(), XbbRefTypeEnum.WORK_ORDER.getAlias()) && Objects.equals(mirrorAppEntity.getSaasMark(), SaasMarkEnum.SAAS.getCode())) {
                        // 安装工单镜像
                        String creatorName = mirrorInstallAllDTO.getLoginUser().getName();
                        AppInstallEntity appInstallEntity = appInstallModel.getByMirrorId(mirrorId, corpid);
                        if (Objects.nonNull(appInstallEntity)) {
                            boolean flag = checkInstall(appInstallEntity);
                            if (flag) {
                                throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403006, MirrorErrorCodeEnum.API_ERROR_403006.getMsg());
                            }
                        }
                        Long appInstallId = installWorkOrderMirror(corpid, mirrorId, sourceCorpid, creatorId, creatorName, mirrorAppEntity);
                        mirrorInstallVO.setId(appInstallId);
                    } else if (Objects.equals(mirrorAppEntity.getSaasMark(), SaasMarkEnum.SAAS.getCode())) {
                        // 安装系统表单镜像
                        beforeInstallApplication(mirrorId, corpid, MirrorApplicationTypeEnum.SYSTEM.getType(), module, mirrorInstallVO, mirrorAppEntity.getAlias());
                        Long appInstallId = installSystemMirror(corpid, mirrorId, sourceCorpid, creatorId, mirrorAppEntity, mirrorInstallDTO);
                        mirrorInstallVO.setId(appInstallId);
                    } else {
                        // 安装自定应用镜像
                        beforeInstallApplication(mirrorId, corpid, MirrorApplicationTypeEnum.CUSTOM.getType(), module, mirrorInstallVO, mirrorAppEntity.getAlias());
                        Long appInstallId = installCustomeMirror(corpid, mirrorId, sourceCorpid, creatorId, mirrorAppEntity, mirrorConfigEntity.getName(), mirrorInstallDTO);
                        mirrorInstallVO.setId(appInstallId);
                    }
                    installVOs.add(mirrorInstallVO);
                } else if (Objects.equals(mirrorConfigEntity.getType(), MirrorTypeEnum.CHART.getType())) {
                    AppInstallEntity appInstallEntity = appInstallModel.getByMirrorId(mirrorId, corpid);
                    if (Objects.nonNull(appInstallEntity)) {
                        boolean flag = checkInstall(appInstallEntity);
                        if (flag) {
                            throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403006, MirrorErrorCodeEnum.API_ERROR_403006.getMsg());
                        }
                    }
                    // 图表安装
                    installChartMirror(mirrorInstallDTO, corpid,mirrorId,sourceCorpid,creatorId);
                }
                mirrorInstallAllVO.setMirrorInstallVOList(installVOs);
            }
        } catch (XbbException e){
            LOG.error("mirrorService installAll error", e);
            throw e;
        } catch (Exception e) {
            LOG.error("mirror installAll error", e);
            throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403004);
        }
        return mirrorInstallAllVO;
    }

    /**
     *
     * @param mirrorId
     * @param corpid
     * @param type  system 系统表单  custom 自定义应用
     * @param module
     * @param mirrorInstallVO
     * @throws XbbException
     * @author xingxing.xiao
     */
    private void beforeInstallApplication(Long mirrorId, String corpid, String type, Boolean module, MirrorInstallVO mirrorInstallVO, String alias) throws XbbException {
        if (Objects.equals(type, MirrorApplicationTypeEnum.CUSTOM.getType())) {
            // 标准版和高级版不能安装自定义应用镜像
            Integer status = packageStatusHelp.getPackageStatus(corpid, ValueAddedTypeEnum.CUSTOM_APPLICATION_NUM, 1);
            if(!Objects.equals(status, PackageStatusEnum.NORMAL.getCode()) && !Objects.equals(status, PackageStatusEnum.WARNING.getCode())){
                throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403045);
            }
        }
        // 1. 查询自定义表单的数量
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("mirrorId", mirrorId);
        param.put("saasMark", SaasMarkEnum.PAAS.getCode());
        param.put("del", DelEnum.NORMAL.getDel());
        Integer customCount = mirrorFormModel.getEntitysCount(param);
        // 2. 根据套餐判断
        if (customCount != 0) {
            Integer allStatus = packageStatusHelp.getPackageStatus(corpid, ValueAddedTypeEnum.CUSTOM_FORM_NUM, customCount);
            if(!Objects.equals(allStatus, PackageStatusEnum.NORMAL.getCode()) && !Objects.equals(allStatus, PackageStatusEnum.WARNING.getCode())){
                if (module) {
                    mirrorInstallVO.setInstallable(0);
                }else {
                    throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403005);
                }
            }
        }
        // 3. 判断镜像应用是否已安装，以及是否可以重新安装镜像
        AppInstallEntity appInstallEntity = appInstallModel.getByMirrorId(mirrorId, corpid);
        if (Objects.nonNull(appInstallEntity)) {
            boolean flag = checkInstall(appInstallEntity);
            if (flag) {
                throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403006, MirrorErrorCodeEnum.API_ERROR_403006.getMsg());
            }
        }
        // 4 市场活动标准版不能被安装
        Boolean standardWithFeeType = packageHelp.isStandardWithFeeType(corpid);
        if (Objects.equals(alias, XbbRefTypeEnum.MARKET_MANAGEMENT.getAlias()) && standardWithFeeType){
            throw  new XbbException(SystemLabelErrorCodeEnum.API_ERROR_1120021, SystemLabelErrorCodeEnum.API_ERROR_1120021.getMsg());
        }
        // 5 工单v2应用，校验工单v2增值套餐
        if (Objects.equals(alias,XbbRefTypeEnum.WORK_ORDER_V2_CENTER.getAlias()) && feeLogModel.workOrderV2PastDue(corpid)) {
            throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403009, MirrorErrorCodeEnum.API_ERROR_403009.getMsg(), SystemAppMenuEnum.WORK_ORDER_V2_CENTER.getName());
        }
    }

    /**
     * 判断是否安装
     * @param appInstallEntity
     * @return false 未安装 true 已安装
     * @throws XbbException
     * @author xingxing.xiao
     */
    private boolean checkInstall(AppInstallEntity appInstallEntity) throws XbbException {
        String corpid = appInstallEntity.getCorpid();
        Long appId = appInstallEntity.getAppId();
        if (Objects.equals(appInstallEntity.getInstallType(), MirrorTypeEnum.APPLICATION.getCode())) {
            if (Objects.equals(appInstallEntity.getMirrorApplicationType(), MirrorApplicationTypeEnum.SYSTEM.getCode())) {
                // 表单镜像回退后并且安装的自定义表单都删除了可重新安装
                List<Long> installFormIds = JSONArray.parseArray(appInstallEntity.getFormIds(), Long.class);

                // 判断安装的表单是否已删除(系统单表单要有回退操作，自定义判断是否删除)
                HashSet<Long> paasFormIds = new HashSet<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                Integer sysCount = 0;
                Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                param.put("corpid", corpid);
                param.put("appId", appId);
                param.put("idIn", installFormIds);
                param.put("del", DelEnum.NORMAL.getDel());
                List<PaasFormEntity> formList = paasFormModel.findEntity(param);
                for (PaasFormEntity entity : formList) {
                    if (Objects.equals(entity.getSaasMark(), SaasMarkEnum.SAAS.getCode())) {
                        sysCount++;
                    } else if (Objects.equals(entity.getSaasMark(), SaasMarkEnum.PAAS.getCode())) {
                        paasFormIds.add(entity.getId());
                    }
                }
                if (sysCount > 0) {
                    // 如果包含系统表单，先判断是否回退
                    boolean flag = Objects.equals(appInstallEntity.getIsToInit(), MirrorRollBackTypeEnum.ROLL_BACK_TO_INIT.getFlag()) || Objects.equals(appInstallEntity.getIsToPrevious(), MirrorRollBackTypeEnum.ROLL_BACK_TO_PREVIOUS.getFlag());
                    if (!flag) {
                        return true;
                    }
                }
                param.clear();
                param.put("corpid", corpid);
                param.put("appId", appId);
                param.put("idIn", paasFormIds);
                param.put("del", DelEnum.NORMAL.getDel());
                Integer count = paasFormModel.getEntitysCount(param);
                if (count > 0) {
                    return true;
                }
            } else if (Objects.equals(appInstallEntity.getMirrorApplicationType(), MirrorApplicationTypeEnum.CUSTOM.getCode())) {
                // 判断安装的应用是否已删除，应用删除之后可重新安装镜像
                PaasAppEntity paasAppEntity = paasAppModel.getByKey(appInstallEntity.getAppId(), corpid);
                if (Objects.nonNull(paasAppEntity) && Objects.equals(paasAppEntity.getDel(), DelEnum.NORMAL.getDel())) {
                    return true;
                }
                // 如果已经删除或回退了，更新tb_app_install表 del = 1
                appInstallEntity.setDel(DelEnum.DELETE.getDel());
                appInstallModel.update(appInstallEntity);
                // 同步删除表单安装信息
                formInstallModel.deleteByAppInstallId(corpid, appInstallEntity.getId());
            } else if (Objects.equals(appInstallEntity.getMirrorApplicationType(), MirrorApplicationTypeEnum.WORK_ORDER.getCode())) {
                // 工单镜像回退后并且安装的其他分类下的工单表单都删除了可重新安装
                List<Long> installFormIds = JSONArray.parseArray(appInstallEntity.getFormIds(), Long.class);

                Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                if (!Objects.equals(appInstallEntity.getIsBack(), MirrorRollBackTypeEnum.NO_OPERATE_BACK_FOR_SYSTEM_APP.getFlag())) {
                    // 查询当前公司的工单默认分类
                    param.put("columns", "id, name");
                    param.put("corpid", corpid);
                    param.put("appId", appId);
                    param.put("isDefault", 1);
                    param.put("del", DelEnum.NORMAL.getDel());
                    List<WorkOrderTemplateCategoryEntity> categoryList = workOrderTemplateCategoryModel.findEntitys(param);

                    // 工单默认分类下的工单模板
                    param.clear();
                    param.put("columns", "id, name, is_default");
                    param.put("corpid", corpid);
                    param.put("appId", appId);
                    param.put("categoryId", categoryList.get(0).getId());
                    param.put("del", DelEnum.NORMAL.getDel());
                    List<WorkOrderTemplateEntity> templateList = workOrderTemplateModel.findEntity(param);

                    // 工单默认分类下的工单表单
                    List<Long> templateIdIn = new ArrayList<>();
                    Long defaultTemaplteId = 0L;
                    for (WorkOrderTemplateEntity entity : templateList) {
                        if (Objects.equals(entity.getIsDefault(), 1)) {
                            defaultTemaplteId = entity.getId();
                        }
                        templateIdIn.add(entity.getId());
                    }
                    List<Long> formIdIn = new ArrayList<>();
                    Long defaultFormId = 0L;
                    param.clear();
                    param.put("columns", "id, name, template_id");
                    param.put("corpid", corpid);
                    param.put("appId", appId);
                    param.put("templateIdIn", templateIdIn);
                    param.put("del", DelEnum.NORMAL.getDel());
                    List<WorkOrderFormEntity> formList = workOrderFormModel.findEntitys(param);
                    for (WorkOrderFormEntity entity : formList) {
                        if (Objects.equals(entity.getTemplateId(), defaultTemaplteId)) {
                            defaultFormId = entity.getId();
                        }
                        formIdIn.add(entity.getId());
                    }

                    if (installFormIds.contains(defaultFormId)) {
                        boolean flag = Objects.equals(appInstallEntity.getIsToInit(), MirrorRollBackTypeEnum.ROLL_BACK_TO_INIT.getFlag()) || Objects.equals(appInstallEntity.getIsToPrevious(), MirrorRollBackTypeEnum.ROLL_BACK_TO_PREVIOUS.getFlag());
                        if (!flag) {
                            return true;
                        }
                        installFormIds.removeAll(formIdIn);
                    }
                }

                param.clear();
                param.put("corpid", corpid);
                param.put("idIn", installFormIds);
                param.put("del", DelEnum.NORMAL.getDel());
                Integer count = workOrderFormModel.getEntitysCount(param);
                if (count > 0) {
                    return true;
                }
            } else {
                throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403017, MirrorErrorCodeEnum.API_ERROR_403017.getMsg());
            }
        } else if (Objects.equals(appInstallEntity.getInstallType(), MirrorTypeEnum.CHART.getCode())) {
            // 判断安装的分组是否被删除
            List<Long> installFormIds = JSONArray.parseArray(appInstallEntity.getFormIds(), Long.class);
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("corpid", corpid);
            param.put("appId", appId);
            param.put("idIn", installFormIds);
            param.put("del", DelEnum.NORMAL.getDel());
            Integer count = chartCategoryModel.getEntitysCount(param);
            if (count > 0) {
                return true;
            }
            // 如果已经删除了，更新tb_app_install表 del = 1
            appInstallEntity.setDel(DelEnum.DELETE.getDel());
            appInstallModel.update(appInstallEntity);
        }
        return false;
    }

    /**
     * 安装图表
     * @param corpid
     * @param mirrorId
     * @param sourceCorpid
     * @param creatorId
     */
    private void installChartMirror(MirrorInstallDTO mirrorInstallDTO, String corpid, Long mirrorId, String sourceCorpid, String creatorId) throws XbbException {
        try {
            // 1. 套餐判断
            HashMap<String,Object > param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("corpid",corpid);
            param.put("mirrorId",mirrorId);
            param.put("del",DelEnum.NORMAL.getDel());
            List<MirrorChartEntity> mirrorChartEntities = mirrorChartModel.findEntitys(param);
            Integer status = packageStatusHelp.getPackageStatus(corpid, ValueAddedTypeEnum.CUSTOM_CHART_NUM, mirrorChartEntities.size());
            if(!Objects.equals(status, PackageStatusEnum.NORMAL.getCode()) && !Objects.equals(status, PackageStatusEnum.WARNING.getCode())){
                throw new XbbException(StatisticsServicesErrorCodeEnum.API_ERROR_224031, StatisticsServicesErrorCodeEnum.API_ERROR_224031.getMsg());
            }

            List<Long> mirrorChartIds = new ArrayList<>();
            mirrorChartEntities.forEach(item -> mirrorChartIds.add(item.getId()));

            // 找出对应表单的所有字段
            HashMap<Long, Map<Integer,  HashSet<String>>> formIdToExplains = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            HashMap<Long, Map<Integer,  HashSet<String>>> workOrderFormIdToExplains = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);

            Map<Long, MirrorChartEntity> mirrorChartMap = getformIdToAttrs(mirrorChartEntities,formIdToExplains,workOrderFormIdToExplains);

            // 2. 前置校验安装
            HashMap<Long,List<FieldAttrEntity>> newformIdToAttrs = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            HashMap<Long,List<FieldAttrEntity>> workOrderNewformIdToAttrs = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            HashMap<Long,String> newForm = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            HashMap<Long,String> workOrderNewForm = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            HashMap<Long,Long> formIdOldToNewMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            HashMap<Long,Long> workOrderFormIdOldToNewMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            // 写个可以区分开的,不然好难找
            beforeInstall(mirrorChartIds, corpid, formIdOldToNewMap,workOrderFormIdOldToNewMap, formIdToExplains,workOrderFormIdToExplains, sourceCorpid,
                    newformIdToAttrs,workOrderNewformIdToAttrs, newForm,workOrderNewForm, mirrorChartMap);

            // 3. 插入图表信息
            long now = DateUtil.getInt();
            //添加分类(parentId==0)
            MirrorChartCategoryEntity mirrorChartCategoryParent = mirrorChartCategoryModel.getByMirrorId(mirrorId, 0L);
            Long sourceParentCategory = mirrorChartCategoryParent.getSourceCategoryId();
            MirrorConfigEntity mirrorConfigEntity = mirrorConfigModel.getByKey(mirrorId);
            ChartCategoryEntity chartCategoryEntity = getChartCategoryEntity(corpid, creatorId, now, mirrorChartCategoryParent,0L);
            //修改图表分类的名称为镜像名称
            chartCategoryEntity.setName(mirrorConfigEntity.getName());
            chartCategoryEntity.setPublish(BasicConstant.ZERO);
            chartCategoryModel.insert(chartCategoryEntity);
            Long id = chartCategoryEntity.getId();
            //添加子分类
            param.clear();
            param.put("mirrorId",mirrorId);
            param.put("del",DelEnum.NORMAL.getDel());
            param.put("negParentId",0L);
            List<MirrorChartCategoryEntity> mirrorChartCategoryModelEntitys = mirrorChartCategoryModel.findEntitys(param);
            // 记录安装的图表分组信息
            JSONArray categoryIds = new JSONArray();
            JSONArray sourceCategoryIds = new JSONArray();

            List<ChartCategoryEntity> chartCategoryEntityList = new ArrayList<>();
            for (MirrorChartCategoryEntity mirrorChartCategoryModelEntity : mirrorChartCategoryModelEntitys) {
                ChartCategoryEntity categoryEntity = getChartCategoryEntity(corpid, creatorId, now, mirrorChartCategoryModelEntity, id);
                categoryEntity.setPublish(BasicConstant.ZERO);
                chartCategoryEntityList.add(categoryEntity);
                sourceCategoryIds.add(mirrorChartCategoryModelEntity.getId());
            }
            chartCategoryModel.insertBatch(chartCategoryEntityList);

            //添加chart
            HashMap<Long,Long> categoryChartMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            for (ChartCategoryEntity categoryEntity : chartCategoryEntityList) {
                categoryChartMap.put(categoryEntity.getSourceCategoryId(),categoryEntity.getId());
                categoryIds.add(categoryEntity.getId());
            }
            List<ChartEntity> chartEntityList = new ArrayList<>();
            for (MirrorChartEntity item : mirrorChartEntities) {
                ChartEntity chartEntity = new ChartEntity();
                changeMirrorChartEntity(item, chartEntity, now, corpid, creatorId, categoryChartMap, formIdOldToNewMap, newformIdToAttrs, newForm, workOrderFormIdOldToNewMap, workOrderNewformIdToAttrs, workOrderNewForm);
                chartEntityList.add(chartEntity);
            }
            ChartMirrorInstallDTO chartMirrorInstallDTO = new ChartMirrorInstallDTO();
            BeanUtil.copyProperties(mirrorInstallDTO, chartMirrorInstallDTO);
            chartMirrorInstallDTO.setChartEntityList(chartEntityList);
            Locale locale = LocaleContextHolder.getLocale();
            String lang = locale.toString();
            chartMirrorInstallDTO.setPlatform(PlatFormEnum.WEB.getValue());
            XbbResponse<ChartMirrorInstallVO> xbbResponse = chartInstallFeignClient.mirrorInstallChart(chartMirrorInstallDTO, lang);
            if (!xbbResponse.isSuccess()) {
                throw new XbbException(StatisticsServicesErrorCodeEnum.API_ERROR_224011,  xbbResponse.getMsg());
            }

            // 记录镜像应用安装信息
            addInstallApp(corpid, mirrorId, creatorId, now, sourceParentCategory, id, sourceCorpid, categoryIds, sourceCategoryIds);
        } catch (XbbException e) {
            LOG.error("mirrorService.installChartMirror 图表安装失败", e);
            throw e;
        } catch (Exception e){
            LOG.error("mirrorService.installChartMirror 图表安装失败", e);
            throw new XbbException(StatisticsServicesErrorCodeEnum.API_ERROR_224011, StatisticsServicesErrorCodeEnum.API_ERROR_224011.getMsg(), "图表安装失败");
        }

    }

    /**
     *  镜像图表信息
     * @param mirrorChartEntities
     * @return formIdToAttrs 表单的所有字段
     * @author guohui.zhang
     */
    private Map<Long, MirrorChartEntity> getformIdToAttrs(List<MirrorChartEntity> mirrorChartEntities,HashMap<Long, Map<Integer, HashSet<String>>> formIdToAttrs,HashMap<Long, Map<Integer, HashSet<String>>> workOrderFormIdToExplains) {
        // HashMap<Long,HashSet<String>> formIdToAttrs = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<Long, MirrorChartEntity> chartEntityMap = new HashMap<>(mirrorChartEntities.size());
        for (MirrorChartEntity mirrorChartEntity : mirrorChartEntities) {
            chartEntityMap.put(mirrorChartEntity.getId(), mirrorChartEntity);
            // 分组
            if (mirrorChartEntity.getGroupAttr() != null) {
                for (Object obj :  mirrorChartEntity.getGroupAttr()) {
                    dealFormAndExplain(formIdToAttrs, (JSONObject) obj,workOrderFormIdToExplains, mirrorChartEntity.getChartType());
                }
            }
            if (mirrorChartEntity.getColGroupAttr() != null) {
                for (Object obj : mirrorChartEntity.getColGroupAttr()) {
                    dealFormAndExplain(formIdToAttrs, (JSONObject) obj, workOrderFormIdToExplains, mirrorChartEntity.getChartType());
                }
            }
            if (mirrorChartEntity.getRowGroupAttr() != null) {
                for (Object obj : mirrorChartEntity.getRowGroupAttr()) {
                    dealFormAndExplain(formIdToAttrs, (JSONObject) obj, workOrderFormIdToExplains, mirrorChartEntity.getChartType());
                }
            }
            // 汇总字段
            if (mirrorChartEntity.getSummaryAttr() != null) {
                for (Object obj : mirrorChartEntity.getSummaryAttr()) {
                    dealFormAndExplain(formIdToAttrs, (JSONObject) obj, workOrderFormIdToExplains, mirrorChartEntity.getChartType());
                }
            }
            // 展示字段
            if (mirrorChartEntity.getDisplayAttr() != null) {
                for (Object obj : mirrorChartEntity.getDisplayAttr()) {
                    dealFormAndExplain(formIdToAttrs, (JSONObject) obj, workOrderFormIdToExplains, mirrorChartEntity.getChartType());
                }
            }
            // 分类：X轴上的分组
            if (mirrorChartEntity.getChartClassify() != null) {
                for (Object obj : mirrorChartEntity.getChartClassify()) {
                    dealFormAndExplain(formIdToAttrs, (JSONObject) obj, workOrderFormIdToExplains, mirrorChartEntity.getChartType());
                }
            }
            // 值：分类与系列组成分组，指标为需要统计的数据，值与Y轴对应；只有数字字段可以作为指标，另外系统自带的计数也可以
            if (mirrorChartEntity.getChartSeries() != null) {
                for (Object obj : mirrorChartEntity.getChartSeries()) {
                    dealFormAndExplain(formIdToAttrs, (JSONObject) obj, workOrderFormIdToExplains, mirrorChartEntity.getChartType());
                }
            }
            // 值：分类与系列组成分组，指标为需要统计的数据，值与Y轴对应；只有数字字段可以作为指标，另外系统自带的计数也可以
            if (mirrorChartEntity.getChartValue() != null) {
                for (Object obj : mirrorChartEntity.getChartValue()) {
                    dealFormAndExplain(formIdToAttrs, (JSONObject) obj,workOrderFormIdToExplains, mirrorChartEntity.getChartType());
                }
            }
            // 筛选条件 不做处理,后期直接置为空
            // for (Object obj : mirrorChartEntity.getFilter()) {
            //     dealFormAndExplain(formIdToAttrs, (JSONObject) obj,workOrderFormIdToExplains);
            // }
            // 穿透展示字段
            if (mirrorChartEntity.getPenetrateAttr() != null) {
                for (Object obj : mirrorChartEntity.getPenetrateAttr()){
                    dealFormAndExplain(formIdToAttrs, (JSONObject) obj,workOrderFormIdToExplains, mirrorChartEntity.getChartType());
                }
            }
        }
        return chartEntityMap;
    }

    /**
     * 找出对应表单的所有字段
     * @param formIdToAttrs
     * @param jsonObj
     * @author guohui.zhang
     */
    private void dealFormAndExplain(HashMap<Long, Map<Integer, HashSet<String>>> formIdToAttrs, JSONObject jsonObj,
                                           HashMap<Long, Map<Integer, HashSet<String>>> workOrderFormIdToExplains, Integer chartType) {
        String attr = jsonObj.getString("attr");
        String originalAttr = jsonObj.getString("originalAttr");
        attr = originalAttr == null ? attr : originalAttr;
        // 对地址的特殊处理
        // if (attr.contains(BusinessConstant.ADDRESS)) {
        //     String substring = attr.substring(0, 9);
        //     attr = substring;
        // }
        Long formId;
        if(Objects.equals(jsonObj.getInteger("businessType"),XbbRefTypeEnum.WORK_ORDER_V2_NODE_LOG_INFO.getCode())){
            return ;
        }
        if(Objects.equals(jsonObj.getInteger("businessType"),XbbRefTypeEnum.WORK_ORDER.getCode())){
            formId = jsonObj.getLong("formId");
            dealSubForm(jsonObj, workOrderFormIdToExplains, attr, formId, chartType);

            if (workOrderFormIdToExplains.containsKey(formId)) {
                if (workOrderFormIdToExplains.get(formId).containsKey(chartType)) {
                    Map<Integer, HashSet<String>> map =  workOrderFormIdToExplains.get(formId);
                    HashSet<String> attrList = map.get(chartType);
                    attrList.add(attr);
                    map.put(chartType, attrList);
                    workOrderFormIdToExplains.put(formId, map);
                } else {
                    Map<Integer, HashSet<String>> chartFieldMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    HashSet<String> attrList = new HashSet<>();
                    attrList.add(attr);
                    chartFieldMap.put(chartType, attrList);
                    workOrderFormIdToExplains.put(formId, chartFieldMap);
                }
            } else {
                HashSet<String> attrList = new HashSet<>();
                attrList.add(attr);
                Map<Integer, HashSet<String>> map = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                map.put(chartType, attrList);
                workOrderFormIdToExplains.put(formId, map);
            }
        }else{
            formId = jsonObj.getLong("formId");
            dealSubForm(jsonObj, formIdToAttrs, attr, formId, chartType);
            if (formIdToAttrs.containsKey(formId)) {
                if (formIdToAttrs.get(formId).containsKey(chartType)) {
                    Map<Integer, HashSet<String>> map = formIdToAttrs.get(formId);
                    HashSet<String> attrList = map.get(chartType);
                    attrList.add(attr);
                    map.put(chartType, attrList);
                    formIdToAttrs.put(formId, map);
                } else {
                    Map<Integer, HashSet<String>> chartFieldMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    HashSet<String> attrList = new HashSet<>();
                    attrList.add(attr);
                    chartFieldMap.put(chartType, attrList);
                    formIdToAttrs.put(formId, chartFieldMap);
                }
            } else {
                HashSet<String> attrList = new HashSet<>();
                attrList.add(attr);
                Map<Integer, HashSet<String>> map = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                map.put(chartType, attrList);
                formIdToAttrs.put(formId, map);
            }
        }
    }

    private void dealSubForm(JSONObject jsonObj, Map<Long, Map<Integer, HashSet<String>>> workOrderFormIdToExplains, String attr, Long formId, Integer chartType) {
        HashSet<String> attrs = new HashSet<>();
        if (attr.contains("subForm_")) {
            JSONObject subForm = jsonObj.getJSONObject("subForm");
            JSONArray item = subForm.getJSONArray("items");
            if (CollectionsUtil.isNotEmpty(item)) {
                for (Object o : item) {
                    JSONObject json = (JSONObject) o;
                    String attr1 = json.getString("attr");
                    attrs.add(attr+"."+attr1);
                }
            }
        }
        if (CollectionsUtil.isEmpty(attrs)) {
            return;
        }
        if (workOrderFormIdToExplains.containsKey(formId)) {
            if (workOrderFormIdToExplains.get(formId).containsKey(chartType)) {
                Map<Integer, HashSet<String>> map =  workOrderFormIdToExplains.get(formId);
                HashSet<String> attrList = map.get(chartType);
                attrList.add(attr);
                map.put(chartType, attrList);
                workOrderFormIdToExplains.put(formId, map);
            } else {
                Map<Integer, HashSet<String>> chartFieldMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                HashSet<String> attrList = new HashSet<>();
                attrList.add(attr);
                chartFieldMap.put(chartType, attrList);
                workOrderFormIdToExplains.put(formId, chartFieldMap);
            }
        } else {
            HashSet<String> attrList = new HashSet<>();
            attrList.add(attr);
            Map<Integer, HashSet<String>> map = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            map.put(chartType, attrList);
            workOrderFormIdToExplains.put(formId, map);
        }
    }

    /**
     * 添加镜像安装记录
     * @param corpid
     * @param mirrorId
     * @param creatorId
     * @param now
     * @param sourceParentCategory
     * @param id
     * @throws XbbException
     */
    private void addInstallApp(String corpid, Long mirrorId, String creatorId, long now, Long sourceParentCategory, Long id, String sourceCorpid, JSONArray categoryIds, JSONArray sourceCategoryIds) throws XbbException {
        AppInstallEntity installEntity = new AppInstallEntity();
        installEntity.setSourceFormIds(sourceCategoryIds.toJSONString());
        installEntity.setFormIds(categoryIds.toJSONString());
        installEntity.setSourceChartCategoryId(sourceParentCategory);
        installEntity.setChartCategoryId(id);
        installEntity.setMirrorId(mirrorId);
        installEntity.setDel(DelEnum.NORMAL.getDel());
        installEntity.setCorpid(corpid);
        installEntity.setAppId(0L);
        installEntity.setCreatorId(creatorId);
        installEntity.setAddTime(now);
        installEntity.setUpdateTime(now);
        installEntity.setType(AppInstallTypeEnum.MIRROR.getType());
        installEntity.setInstallType(MirrorTypeEnum.CHART.getCode());
        installEntity.setSourceAppId(0L);
        installEntity.setSourceCorpid(sourceCorpid);
        installEntity.setIsToPrevious(MirrorRollBackTypeEnum.NO_ROLL_BACK.getFlag());
        installEntity.setIsToInit(MirrorRollBackTypeEnum.NO_ROLL_BACK.getFlag());
        appInstallModel.insert(installEntity);
    }

    private ChartCategoryEntity getChartCategoryEntity(String corpid, String creatorId, long now, MirrorChartCategoryEntity mirrorChartCategory,Long parentId) throws XbbException  {
            ChartCategoryEntity chartCategoryEntity = new ChartCategoryEntity();
            BeanUtil.copyProperties(mirrorChartCategory,chartCategoryEntity);
            chartCategoryEntity.setId(null);
            chartCategoryEntity.setCorpid(corpid);
            chartCategoryEntity.setParentId(parentId);
            chartCategoryEntity.setAddTime(now);
            chartCategoryEntity.setUpdateTime(now);
            chartCategoryEntity.setCreatorId(creatorId);
            chartCategoryEntity.setSourceCategoryId(mirrorChartCategory.getSourceCategoryId());
            chartCategoryEntity.setName(mirrorChartCategory.getName());
            Integer maxSort= getChartCategorySort(corpid);
            chartCategoryEntity.setSort(maxSort+1);
            return chartCategoryEntity;
    }

    private Integer getChartCategorySort(String corpid) throws XbbException {
        Integer sort = 0;
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.clear();
        param.put("columns", "id, sort");
        param.put("corpid", corpid);
        param.put("orderByStr", "sort desc");
        param.put("del", DelEnum.NORMAL.getDel());
        param.put("parentId",0L);
        List<ChartCategoryEntity> entitys = chartCategoryModel.findEntitys(param);
        if(Objects.isNull(entitys)){
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.APP_NOT_EXIST);
        }
        ChartCategoryEntity chartCategoryEntity= entitys.get(0);
        Integer count = entitys.size();
        if (chartCategoryEntity.getSort() > count) {
            sort = chartCategoryEntity.getSort();
        }
        return sort;
    }

    private void changeMirrorChartEntity(MirrorChartEntity item, ChartEntity chartEntity,Long now,String corpid ,String creatorId,HashMap<Long,Long> categoryChartMap,HashMap<Long,Long> formIdOldToNewMap,HashMap<Long,List<FieldAttrEntity>> newformIdToAttrs,HashMap<Long,String> newForm ,HashMap<Long,Long> workOrderFormIdOldToNewMap,HashMap<Long,List<FieldAttrEntity>> workOrderNewformIdToAttrs,HashMap<Long,String> workOrderNewForm) throws XbbException {
        JSONObject driverSources =item.getDriverSources();
        Integer businessType = driverSources.getInteger("businessType");
        if (Objects.equals(businessType,XbbRefTypeEnum.WORK_ORDER.getCode())){
            updateChartEntity(item, chartEntity, now, corpid, creatorId, categoryChartMap, workOrderFormIdOldToNewMap, workOrderNewformIdToAttrs, workOrderNewForm);
        }else{
            updateChartEntity(item, chartEntity, now, corpid, creatorId, categoryChartMap, formIdOldToNewMap, newformIdToAttrs, newForm);
        }
        if (Objects.equals(businessType,XbbRefTypeEnum.WORK_ORDER_V2_NODE_LOG_INFO.getCode())){
            JSONArray slaveSources = chartEntity.getSlaveSources();
            if (CollectionsUtil.isNotEmpty(slaveSources)) {
                JSONObject jsonObject = slaveSources.getJSONObject(0);
                Long formId = jsonObject.getLong(StringConstant.FORM_ID);
                BaseIdListDTO baseIdListDTO = new BaseIdListDTO();
                baseIdListDTO.setPlatform(PlatFormEnum.WEB.getValue());
                baseIdListDTO.setCorpid(corpid);
                baseIdListDTO.setUserId(creatorId);
                UserVO userVO = new UserVO();
                userVO.setCorpid(corpid);
                userVO.setUserId(creatorId);
                baseIdListDTO.setLoginUser(userVO);
                baseIdListDTO.setIdList(Collections.singletonList(formId));
                String response = workOrderV2FeignClient.getEnableProcess(baseIdListDTO, LocaleContextHolder.getLocale().toString());
                JSONObject responseJson = JsonHelperUtil.parseObject(response);
                if (ErrorCodeEnum.API_SUCCESS.getCode() != (int)responseJson.get(StringConstant.CODE)) {
                    throw new XbbException(responseJson.getIntValue(StringConstant.CODE), responseJson.getString(StringConstant.KEY_ERROR_MSG));
                }
                Long processId = JsonHelperUtil.parseObject(responseJson.getString("result"),Long.class);
                String filter = "[{\"attr\":\"versionId\",\"memo\":\"\",\"sort\":3,\"fixed\":false,\"value\":["+processId+"],\"formId\":0,\"isOpen\":1,\"symbol\":\"equal\",\"redFlag\":0,\"setType\":1,\"visible\":1,\"accuracy\":2,\"attrName\":\"版本Id\",\"attrType\":\"num\",\"editHide\":0,\"editable\":1,\"noRepeat\":0,\"required\":1,\"saasAttr\":\"versionId\",\"showType\":0,\"comboType\":0,\"fieldType\":2,\"cancelFlag\":0,\"noEditable\":0,\"recentTime\":false,\"screenType\":3,\"defaultAttr\":{\"rely\":{\"linkField\":\"\",\"targetField\":\"\",\"displayField\":\"\"},\"defaultValue\":\"\"},\"defaultName\":\"版本Id\",\"integerOnly\":0,\"isRedundant\":0,\"isSingleRow\":0,\"businessType\":20350,\"editableRule\":{\"dep\":[],\"role\":[],\"user\":[],\"relative\":[],\"roleList\":[],\"userList\":[]},\"originalAttr\":\"versionId\",\"designEditable\":0,\"numericalLimits\":{\"max\":9007199254740991,\"min\":-9007199254740991},\"strictController\":0,\"visibleScopeRule\":{\"dep\":[],\"role\":[],\"user\":[],\"relative\":[],\"roleList\":[],\"userList\":[]},\"visibleScopeEnable\":0,\"numericalLimitsFlag\":0}]";
                chartEntity.setFilter(JSONObject.parseArray(filter));
            }
        }

    }

    private void updateChartEntity(MirrorChartEntity item, ChartEntity chartEntity, Long now, String corpid, String creatorId, HashMap<Long, Long> categoryChartMap, HashMap<Long, Long> formIdOldToNewMap, HashMap<Long, List<FieldAttrEntity>> newformIdToAttrs, HashMap<Long, String> newForm) {
        BeanUtil.copyProperties(item, chartEntity);
        // 分组字段
//        putNewFormId(formIdOldToNewMap, chartEntity.getGroupAttr(), newformIdToAttrs, newForm, corpid);
        putNewFormId(formIdOldToNewMap, chartEntity.getRowGroupAttr(), newformIdToAttrs, newForm, corpid);
        putNewFormId(formIdOldToNewMap, chartEntity.getColGroupAttr(), newformIdToAttrs, newForm, corpid);
        // 汇总字段
        putNewFormId(formIdOldToNewMap, chartEntity.getSummaryAttr(), newformIdToAttrs, newForm, corpid);
        // 展示字段
        putNewFormId(formIdOldToNewMap, chartEntity.getDisplayAttr(), newformIdToAttrs, newForm, corpid);
        // 穿透展示字段
        putNewFormId(formIdOldToNewMap, chartEntity.getPenetrateAttr(), newformIdToAttrs, newForm, corpid);
        // 分类：X轴上的分组，单选，格式为{"attr": "name", "sumWay": 1, "attrType":1}
        putNewFormId(formIdOldToNewMap, chartEntity.getChartClassify(), newformIdToAttrs, newForm, corpid);
        // 系列：在分类的基础上，对分类字段的值再次进行分组，可多选.格式为：[{"attr": "name", "sumWay": 1, "attrType":1}]
        putNewFormId(formIdOldToNewMap, chartEntity.getChartSeries(), newformIdToAttrs, newForm, corpid);
        // 计算字段
        putNewFormId(formIdOldToNewMap, chartEntity.getComputedNumAttr(), newformIdToAttrs, newForm, corpid);
        putNewFormId(formIdOldToNewMap, chartEntity.getComputedAttr(), newformIdToAttrs, newForm, corpid);
        //查询条件
        putNewFormIdForSearch(formIdOldToNewMap, chartEntity.getSearch(), newformIdToAttrs, newForm, corpid);
        // 值：分类与系列组成分组，指标为需要统计的数据，值与Y轴对应；
        putNewFormId(formIdOldToNewMap, chartEntity.getChartValue(), newformIdToAttrs, newForm, corpid);
        JSONObject permission = new JSONObject();
        // 高级权限不可见
        permission.put("visible", 0);
        permission.put("visibleScopeEnable", 0);
        VisibleRulePoJo visibleRulePoJo = new VisibleRulePoJo();
        visibleRulePoJo.setType(VisibleTypeEnum.SOME_PEOPLE_CAN_SEE.getType());
        visibleRulePoJo.setUser(new ArrayList<>());
        visibleRulePoJo.setDep(new ArrayList<>());
        visibleRulePoJo.setRole(new ArrayList<>());
        permission.put("visibleScopeRule", visibleRulePoJo);
        // 高级权限不可导出
        permission.put("export", 0);
        permission.put("exportAdvanceEnable", 0);
        VisibleRulePoJo exportRulePoJo = new VisibleRulePoJo();
        exportRulePoJo.setType(VisibleTypeEnum.SOME_PEOPLE_CAN_SEE.getType());
        exportRulePoJo.setUser(new ArrayList<>());
        exportRulePoJo.setDep(new ArrayList<>());
        exportRulePoJo.setRole(new ArrayList<>());
        permission.put("exportRule", exportRulePoJo);
        chartEntity.setPermissions(permission);
        JSONArray jsonArray = new JSONArray();
        chartEntity.setFilter(jsonArray);
        // putNewFormId(formIdOldToNewMap, chartEntity.getFilter(), newformIdToAttrs, newForm);
        JSONObject driverSources = item.getDriverSources();
        changeDataSourceAppIdMenuIdFormId(driverSources, formIdOldToNewMap, corpid);
        chartEntity.setDriverSources(driverSources);
        if (!Objects.equals(item.getSlaveSources().size(), BasicConstant.ZERO)) {
            JSONArray slaveSources = item.getSlaveSources();
            for (Object slaveSource : slaveSources) {
                JSONObject slave = (JSONObject) slaveSource;
                changeDataSourceAppIdMenuIdFormId(slave, formIdOldToNewMap, corpid);
            }
            chartEntity.setSlaveSources(slaveSources);
        }
        JSONArray categoryArray = new JSONArray();
        JSONArray sourceCategory = item.getSourceCategory();
        //多个categoryid存在的情况,(staticType= 1 and chartType =15)
        for (Object obj : sourceCategory) {
            long sourceCategoryId = Long.parseLong(obj.toString());
            Long aLong = categoryChartMap.get(sourceCategoryId);
            categoryArray.add(aLong);
        }
        chartEntity.setCategory(categoryArray);
        //表头冻结字段
        JSONObject explains = item.getExplains();
        JSONObject frozeColum = explains.getJSONObject("frozeColum");
        if (frozeColum.size()>0) {
            delFrozeColum(formIdOldToNewMap, frozeColum, newformIdToAttrs, newForm, corpid);
        }
        explains.put("frozeColum",frozeColum);
        JSONObject position = explains.getJSONObject("position");
        JSONObject newPosition = new JSONObject();
        position.forEach((key, value) -> {
            JSONObject obj = (JSONObject) value;
            newPosition.put(categoryArray.getString(0), obj);
        });
        explains.put("position", newPosition);
        chartEntity.setExplains(explains);
        chartEntity.setId(null);
        chartEntity.setAddTime(now);
        chartEntity.setUpdateTime(now);
        chartEntity.setCorpid(corpid);
        chartEntity.setCreatorId(creatorId);
    }

    /**
     * 处理表头冻结字段
     * @param formIdOldToNewMap
     * @param frozeColum
     * @param newformIdToAttrs
     * @param newForm
     * @param corpid
     */
    private void delFrozeColum(HashMap<Long, Long> formIdOldToNewMap, JSONObject frozeColum, HashMap<Long, List<FieldAttrEntity>> newformIdToAttrs, HashMap<Long, String> newForm, String corpid) {
        Long formId = frozeColum.getLong("formId");
        Long newFormId = formIdOldToNewMap.get(formId);
        Integer businessType = frozeColum.getInteger("businessType");
        String attr = frozeColum.getString("attr");
        List<FieldAttrEntity> fieldAttrEntities = newformIdToAttrs.get(newFormId);
        for (FieldAttrEntity item : fieldAttrEntities) {
            if (Objects.equals(attr, item.getAttr())) {
                String attrName = item.getAttrName();
                frozeColum.put("attrName", attrName);
                String formName = newForm.get(newFormId);
                frozeColum.put("label", formName + attrName);
                frozeColum.put("sort",0);
            }
        }
        frozeColum.put("formId", newFormId);
    }

    private void putNewFormIdForSearch(HashMap<Long, Long> formIdOldToNewMap, JSONArray search, HashMap<Long, List<FieldAttrEntity>> newformIdToAttrs, HashMap<Long, String> newForm, String corpid) {
        for (Object obj : search) {
            JSONObject json = (JSONObject) obj;
            Long formId = json.getLong("formId");
            Long newFormId = formIdOldToNewMap.get(formId);
            Integer businessType = json.getInteger("businessType");
            Long appId;
            Long menuId;
            if (Objects.equals(businessType, XbbRefTypeEnum.WORK_ORDER.getCode())) {
                WorkOrderFormEntity formEntity = workOrderFormModel.getByKey(newFormId, corpid);
                appId = formEntity.getAppId();
                menuId = formEntity.getMenuId();
            } else {
                PaasFormEntity formEntity = paasFormModel.getByKey(newFormId, corpid);
                appId = formEntity.getAppId();
                menuId = formEntity.getMenuId();
            }
            String attr = json.getString("attr");
            json.put("parentId", newFormId.toString());
            List<FieldAttrEntity> fieldAttrEntities = newformIdToAttrs.get(newFormId);
            fieldAttrEntities.forEach(item -> {
                if (Objects.equals(attr, item.getAttr())) {
                    json.put("appid", appId);
                    json.put("formId", newFormId);
                    JSONObject rely = json.getJSONObject("defaultAttr").getJSONObject("rely");
                    if (Objects.nonNull(rely)) {
                        rely.put("sourceFormId", newFormId);
                        rely.put("sourceMenuId", menuId);
                        rely.put("sourceAppId", appId);
                    }
                }
            });

            json.put("formId", newFormId);
        }
    }

    private void putNewFormId(HashMap<Long, Long> formIdOldToNewMap, JSONArray groupAttr,HashMap<Long,List<FieldAttrEntity>> newFormIdToAttrs,HashMap<Long,String> newForm ,String corpid ) {
        if (CollectionsUtil.isEmpty(groupAttr)) {
            return;
        }
        for (int i = 0; i < groupAttr.size(); i++) {
            JSONObject json = groupAttr.getJSONObject(i);
            Long formId = json.getLong("formId");
            Long newFormId = formIdOldToNewMap.get(formId);
            Integer businessType = json.getInteger("businessType");
            Long appId;
            if (Objects.equals(businessType,XbbRefTypeEnum.WORK_ORDER_V2_NODE_LOG_INFO.getCode()) || Objects.isNull(newFormId)) {
                continue;
            }
            if (Objects.equals(businessType,XbbRefTypeEnum.WORK_ORDER.getCode())) {
               appId = workOrderFormModel.getByKey(newFormId, corpid).getAppId();
            }else{
                appId = paasFormModel.getByKey(newFormId, corpid).getAppId();
            }
            String originalAttr = json.getString("originalAttr");
            String attr = originalAttr == null ? json.getString("attr") : originalAttr;
            List<FieldAttrEntity> fieldAttrEntities = newFormIdToAttrs.get(newFormId);
            Boolean flag = true;
            for (FieldAttrEntity fieldAttrEntity : fieldAttrEntities) {
                if (Objects.equals(fieldAttrEntity.getAttr(), "dataId")) {
                    flag = false;
                }
            }
            for (FieldAttrEntity item : fieldAttrEntities) {
                if (Objects.equals(attr, item.getAttr())) {
                    String attrName = item.getAttrName();
                    json.put("attrName", attrName);
                    if (StringUtil.isNotEmpty(item.getDefaultName())) {
                        json.put("defaultName", item.getDefaultName());
                    }
                    String formName = newForm.get(newFormId);
                    if (flag && !Objects.equals(item.getFieldType(), FieldTypeEnum.SUB_FORM.getType())) {
                        json.put("label", formName + attrName);
                        json.put("labelName", formName + "--" + item.getAttrName());
                    }
                    Long parentId = json.getLong("parentId");
                    Long aLong = formIdOldToNewMap.get(parentId);
                    json.put("parentId", String.valueOf(aLong));
                }
                String[] split = item.getAttr().split("\\.");
                if (split.length >= 2) {
                    if (split[0].contains("subForm_")) {
                        if (!Objects.equals(json.getJSONObject("subForm"), null)) {
                            JSONArray jsonArray = json.getJSONObject("subForm").getJSONArray("items");
                            jsonArray.forEach(subForm -> {
                                JSONObject jsonObject = (JSONObject) subForm;
                                jsonObject.put("formId", newFormId);
                                jsonObject.put("parentId", newFormId);
                                jsonObject.put("appId", appId);
                            });
                        }

                    }
                }
            }

            json.put("parentId", newFormId);
            json.put("formId", newFormId);
            JSONObject linkedAttr = json.getJSONObject("linkedAttr");
            if (linkedAttr != null) {
                linkedAttr.put("formId", newFormId);
            }
        }
    }

    private void changeDataSourceAppIdMenuIdFormId(JSONObject driverSources, HashMap<Long, Long> formIdOldToNewMap, String corpid) {
        if (Objects.equals(driverSources.get("businessType"), XbbRefTypeEnum.WORK_ORDER_V2_NODE_LOG_INFO.getCode())) {
            return ;
        }
        HashMap<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("id", formIdOldToNewMap.get(driverSources.getLong("formId")));
        param.put("del", DelEnum.NORMAL.getDel());
        param.put("corpid", corpid);
        Integer formIdInteger = (Integer) driverSources.get("formId");
        long formId = formIdInteger.longValue();
        if (!Objects.equals(driverSources.get("businessType"), XbbRefTypeEnum.WORK_ORDER.getCode())) {
            PaasFormEntityExt paasFormEntityExt = paasFormModel.getByKey(formIdOldToNewMap.get(formId), corpid);
            driverSources.put("appId", paasFormEntityExt.getAppId());
            driverSources.put("formId", paasFormEntityExt.getId());
            // 单表单关联
            if (!Objects.equals(driverSources.getInteger("fieldType"), FieldTypeEnum.LINK_DATA.getType()) && !Objects.equals(driverSources.getInteger("fieldType"), FieldTypeEnum.DATAID.getType())) {
                driverSources.put("name", paasFormEntityExt.getName());
            }
            driverSources.put("menuId", paasFormEntityExt.getMenuId());
        } else {
            WorkOrderFormEntityExt workOrderFormEntityExt = workOrderFormModel.getByKey(formIdOldToNewMap.get(formId), corpid);
            if (!Objects.equals(driverSources.getInteger("fieldType"), FieldTypeEnum.LINK_DATA.getType()) && !Objects.equals(driverSources.getInteger("fieldType"), FieldTypeEnum.DATAID.getType())) {
                driverSources.put("name", workOrderFormEntityExt.getName());
            }
            driverSources.put("appId", workOrderFormEntityExt.getAppId());
            driverSources.put("formId", workOrderFormEntityExt.getId());
            driverSources.put("menuId", workOrderFormEntityExt.getMenuId());
        }
    }

    /**
     * 图表前置校验
     * @param chartIds
     * @param formIdOldToNewMap
     * @param formIdToAttrs 表单对应的所有字段
     * @throws XbbException
     */
    private void  beforeInstall(List<Long> chartIds,String corpid, HashMap<Long,Long> formIdOldToNewMap ,HashMap<Long,Long> workOrderFormIdOldToNewMap,
                                HashMap<Long,Map<Integer,  HashSet<String>>> formIdToAttrs ,HashMap<Long,Map<Integer,  HashSet<String>>> workOrderFormIdToAttrs,
                                String sourceCorpid,HashMap<Long,List<FieldAttrEntity>> newformIdToAttrs ,HashMap<Long,List<FieldAttrEntity>> workOrderNewformIdToAttrs,
                                HashMap<Long,String> newForm ,HashMap<Long,String> workOrderNewForm, Map<Long, MirrorChartEntity> mirrorChartMap) throws  XbbException {
        try {
            HashMap<String,Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("chartIdIn",chartIds);
            param.put("del",DelEnum.NORMAL.getDel());
            List<ChartBindAppInfoEntity> chartBindAppInfoEntities = chartBindAppInfoModel.findEntitys(param);

            HashSet<Long> workOrderAppIds = new HashSet<>();
            HashSet<Long> workOrderMenuIds = new HashSet<>();
            HashSet<Long> workOrderFormIds = new HashSet<>();

            // 原图表应用id
            HashSet<Long> appIds=new HashSet<>();
            // 原图表menuId
            HashSet<Long> menuIds=new HashSet<>();
            // 原图表formId
            HashSet<Long> formIds = new HashSet<>();
            // 这个参数可以去掉,用不着
            HashSet<Long> customFormIds = new HashSet<>();
            // 记录app与businessTyoe的关系
            HashMap<Long,HashSet<Integer>> appBusinessType = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            // 记录form与businessType关系
            HashMap<Long,Integer> formBusinessType = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            // 这个也可以去掉,不用关系
            HashMap<Integer,HashSet<Long>> businessTypeFormId = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);

            chartBindAppInfoEntities.forEach(item -> {
                //工单单独做处理
                if (Objects.equals(item.getDriverBusinessType(), XbbRefTypeEnum.WORK_ORDER.getCode())) {
                    workOrderAppIds.add(item.getDriverAppId());
                    workOrderMenuIds.add(item.getDriverMenuId());
                    workOrderFormIds.add(item.getDriverFormId());
                }else{
                    appIds.add(item.getDriverAppId());
                    menuIds.add(item.getDriverMenuId());
                    if (!Objects.equals(item.getDriverBusinessType(), XbbRefTypeEnum.WORK_ORDER_V2_NODE_LOG_INFO.getCode())) {
                        formIds.add(item.getDriverFormId());
                        dealFormIdsAndAppIds(appIds, formIds,customFormIds, appBusinessType, formBusinessType,businessTypeFormId, item.getDriverFormId(),item.getDriverAppId(),item.getDriverBusinessType(),item.getDriverSaasMark());
                        dealExplains(item.getChartId(),   mirrorChartMap, formIdToAttrs, item.getDriverFormId(),item.getFirstLinkAttr(),item.getSecondLinkAttr(),item.getDriverSaasMark());
                    }
                    if (Objects.nonNull(item.getSlaveFirstAppId())){
                        appIds.add(item.getSlaveFirstAppId());
                        menuIds.add(item.getSlaveFirstMenuId());
                        formIds.add(item.getSlaveFirstFormId());
                        dealFormIdsAndAppIds(appIds, formIds, customFormIds, appBusinessType, formBusinessType,businessTypeFormId, item.getSlaveFirstFormId(),item.getSlaveFirstAppId(),item.getSlaveFirstBusinessType(),item.getSlaveFirstSaasMark());
                        if (!Objects.equals(item.getDriverBusinessType(), XbbRefTypeEnum.WORK_ORDER_V2_NODE_LOG_INFO.getCode())) {
                            dealExplains(item.getChartId(),   mirrorChartMap,formIdToAttrs,item.getSlaveFirstFormId(),item.getSlaveFirstLinkAttr(),null,item.getSlaveFirstSaasMark());
                        }
                        if (Objects.equals(item.getDriverBusinessType(), XbbRefTypeEnum.WORK_ORDER_V2_NODE_LOG_INFO.getCode()) && Objects.equals(0L, item.getSlaveFirstFormId())) {
                            formIdToAttrs.remove(item.getSlaveFirstFormId());
                        }
                        if (Objects.nonNull(item.getSlaveSecondAppId()) ){
                            appIds.add(item.getSlaveSecondAppId());
                            menuIds.add(item.getSlaveSecondMenuId());
                            formIds.add(item.getSlaveSecondFormId());
                            dealFormIdsAndAppIds(appIds, formIds,customFormIds, appBusinessType, formBusinessType,businessTypeFormId, item.getSlaveSecondFormId(),item.getSlaveSecondAppId(),item.getSlaveSecondBusinessType(),item.getSlaveSecondSaasMark());
                            dealExplains(item.getChartId(),   mirrorChartMap,formIdToAttrs,item.getSlaveSecondFormId(),item.getSlaveSecondLinkAttr(),null,item.getSlaveSecondSaasMark());
                        }
                    }
                }
            });
            // 工单校验
            if (workOrderAppIds.size() > 0) {
                checkWorkOrder(workOrderAppIds, workOrderFormIds, workOrderMenuIds, workOrderFormIdToAttrs, workOrderFormIdOldToNewMap, corpid, sourceCorpid, workOrderNewformIdToAttrs, workOrderNewForm);
            }
            if (!Objects.equals(appIds.size(), BasicConstant.ZERO)) {
                //校验应用是否安装(系统表单顺表就校验了)
                HashSet<Long> appInstallIds = checkApp(appIds, appBusinessType, corpid,sourceCorpid);
                //校验表单是否被开启
                checkForm(formIds, corpid, formBusinessType, appInstallIds, formIdToAttrs, formIdOldToNewMap, newForm);
                //表单缺少字段
                checkformIdToAttrs(formIdToAttrs, corpid, newformIdToAttrs);
            }
        }catch (XbbException e) {
            LOG.error("mirrorService图表前置校验未通过", e);
            throw e;
        } catch (Exception e){
            LOG.error("图表前置校验未通过", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001,SystemErrorCodeEnum.API_ERROR_100001.getMsg() );
        }
    }

    /**
     * 问题1: 如何判断是否关联默认工单表单
     * @param workOrderAppIds
     * @param workOrderFormIds
     * @param workOrderMenuIds
     * @param formIdToAttrs
     * @param formIdOldToNewMap
     * @param corpid
     * @param sourceCorpid
     * @param newformIdToAttrs
     * @param newForm
     * @throws XbbException
     */
    private void checkWorkOrder(HashSet<Long> workOrderAppIds, HashSet<Long> workOrderFormIds, HashSet<Long> workOrderMenuIds, HashMap<Long, Map<Integer, HashSet<String>>> formIdToAttrs,
                                HashMap<Long, Long> formIdOldToNewMap,String corpid,String sourceCorpid,HashMap<Long,List<FieldAttrEntity>> newformIdToAttrs,HashMap<Long,String> newForm) throws  XbbException{

        // 1. 处理表单字段(isBack 此处的逻辑是基于只能回退最新的镜像, 如果后期回退逻辑更改,注意此处要做变动)
        HashSet<Long> installFormIds = new HashSet<>() ;
        HashMap<String,Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("corpid",corpid);
        param.put("mirrorApplicationType", MirrorApplicationTypeEnum.WORK_ORDER.getCode());
        param.put("sourceAppIdIn",workOrderAppIds);
        param.put("isToPrevious", MirrorRollBackTypeEnum.NO_ROLL_BACK_TO_PREVIOUS.getFlag());
        param.put("isBack", MirrorRollBackTypeEnum.OPERATE_BACK.getFlag());
        param.put("del",DelEnum.NORMAL.getDel());
        List<AppInstallEntity> appInstallEntities = appInstallModel.findEntitys(param);
        if (CollectionsUtil.isNotEmpty(appInstallEntities)) {
            param.clear();
            param.put("corpid", corpid);
            param.put("appInstallId", appInstallEntities.get(0).getId());
            param.put("sourceFormIdIn", workOrderFormIds);
            // param.put("del", DelEnum.NORMAL.getDel());
            List<FormInstallEntity> formInstallEntities = formInstallModel.findEntitys(param);
            if (CollectionsUtil.isNotEmpty(formInstallEntities)) {
                for (FormInstallEntity formInstallEntity : formInstallEntities) {
                    Long sourceFormId = formInstallEntity.getSourceFormId();
                    Long formId = formInstallEntity.getFormId();
                    installFormIds.add(formId);
                    formIdOldToNewMap.put(sourceFormId, formId);
                    // 当前公司工单表单应有的字段
                    formIdToAttrs.put(formId, formIdToAttrs.get(sourceFormId));
                    formIdToAttrs.remove(sourceFormId);
                }
            }
        }
        param.clear();
        param.put(StringConstant.CORPID,sourceCorpid);
        param.put("idIn", workOrderFormIds);
        List<WorkOrderFormEntityExt> sourceWorkOrderFormList = workOrderFormModel.findEntitysExt(param);
        List<WorkOrderFormEntityExt> workOrderFormList = new ArrayList<>();
        // 当前公司默认模板对应的默认表单
        if (installFormIds.isEmpty()) {
            param.clear();
            param.put(StringConstant.CORPID, corpid);
            param.put("del", DelEnum.NORMAL.getDel());
            param.put("isDefault", BasicConstant.ONE);
            List<WorkOrderTemplateEntity> templateEntityList = workOrderTemplateModel.findEntity(param);
            if(CollectionUtils.isEmpty(templateEntityList)){
                throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403031, MirrorErrorCodeEnum.API_ERROR_403031.getMsg(), XbbRefTypeEnum.WORK_ORDER_CENTER.getName());
            }
            WorkOrderFormEntityExt defaultFormEntity = workOrderFormModel.getByTemplateId(templateEntityList.get(0).getId(), corpid);
            if (Objects.isNull(defaultFormEntity)) {
                throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403023, MessageConstant.FORM_NOT_EXIST);
            }
            workOrderFormList.add(defaultFormEntity);
            installFormIds.add(defaultFormEntity.getId());
            sourceWorkOrderFormList.forEach(sourceWorkOrderFormEntity->{
                // 提供镜像公司的默认模板下的默认表单
                // 判断是否关联了默认表单(售后)
                Long sourceFormId = sourceWorkOrderFormEntity.getId();
                if (workOrderFormIds.contains(sourceFormId)) {
                    formIdToAttrs.put(defaultFormEntity.getId(), formIdToAttrs.get(sourceFormId));
                    formIdToAttrs.remove(sourceFormId);
                    installFormIds.add(defaultFormEntity.getId());
                    formIdOldToNewMap.put(sourceFormId, defaultFormEntity.getId());
                }
            });
        } else {
            //查询对应的表单
            param.clear();
            param.put(StringConstant.CORPID, corpid);
            param.put("idIn", installFormIds);
            workOrderFormList = workOrderFormModel.findEntitysExt(param);
            if (workOrderFormList.size() < sourceWorkOrderFormList.size()) {
                // 关联了不存在的工单表单
                throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403023, MirrorErrorCodeEnum.API_ERROR_403023.getMsg());
            }
        }

        for (WorkOrderFormEntity workOrderFormEntity : workOrderFormList) {
            newForm.put(workOrderFormEntity.getId(),workOrderFormEntity.getName());
            //如果表单未开启或已删除
            if (Objects.equals(workOrderFormEntity.getDel(),DelEnum.DELETE.getDel())){
                //表单被删除
                throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403035, MirrorErrorCodeEnum.API_ERROR_403035.getMsg());
            }
            if (Objects.equals(workOrderFormEntity.getEnable(),BasicConstant.ZERO)){
                //表单未启用
                throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403025, MirrorErrorCodeEnum.API_ERROR_403025.getMsg(), workOrderFormEntity.getName());
            }
        }

        param.clear();
        param.put("formIdIn",installFormIds);
        param.put("del",DelEnum.NORMAL.getDel());
        param.put("corpid",corpid);
        List<WorkOrderExplainEntity> workOrderExplainEntities = workOrderExplainModel.findEntitys(param);
        for (WorkOrderExplainEntity workOrderExplainEntity : workOrderExplainEntities) {
            //表单解释是否包含所有的解释
            List<FieldAttrEntity> fieldAttrEntities = JSON.parseArray(workOrderExplainEntity.getExplains(), FieldAttrEntity.class);
            List<String> attrList =new ArrayList<>();
            Map<Integer, HashSet<String>> strings = formIdToAttrs.get(workOrderExplainEntity.getFormId());
            if (Objects.nonNull(strings)) {
                List<FieldAttrEntity> newFieldAttrEntities = new ArrayList<>();
                fieldAttrEntities.forEach(item -> {
                    // if (Objects.nonNull(item.getSubForm())) {
                    if (ExplainUtil.isSubForm(item.getFieldType())) {
                        SubFormPoJo subForm = item.getSubForm();
                        List<? extends FieldAttrEntity> items = subForm.getItems();
                        for (FieldAttrEntity subForms : items) {
                            String attr = subForms.getAttr();
                            attrList.add(item.getAttr() + "." + attr);
                            attrList.add(item.getAttr());
                            FieldAttrEntity fieldAttrEntity = new FieldAttrEntity();
                            fieldAttrEntity.setIsRedundant(100);
                            fieldAttrEntity.setAttr(item.getAttr() + "." + attr);
                            newFieldAttrEntities.add(fieldAttrEntity);
                        }
                    }else if(Objects.equals(item.getFieldType(),FieldTypeEnum.ADDRESS.getType())){
                        dealAddress(newFieldAttrEntities, strings, item.getAttr(), item.getAttrName(), item.getFieldType(), item);
                        dealAddress(newFieldAttrEntities, strings, item.getAttr()+".province",I18nMessageUtil.getMessage(AddressEnum.PROVINCE.getName()), item.getFieldType(), item);
                        // address_*.city
                        dealAddress(newFieldAttrEntities, strings, item.getAttr()+".city", I18nMessageUtil.getMessage(AddressEnum.CITY.getName()), item.getFieldType(), item);
                        //address_*.address
                        dealAddress(newFieldAttrEntities, strings, item.getAttr()+".address", I18nMessageUtil.getMessage(AddressEnum.ADDRESS.getName()), item.getFieldType(), item);
                        // address_*.district
                        dealAddress(newFieldAttrEntities, strings, item.getAttr()+".district", I18nMessageUtil.getMessage(AddressEnum.DISTRICT.getName()), item.getFieldType(), item);
                        attrList.add(item.getAttr());
                        attrList.add(item.getAttr()+ StringConstant.POINT + BusinessConstant.PROVINCE);
                        attrList.add(item.getAttr()+ StringConstant.POINT + BusinessConstant.CITY);
                        attrList.add(item.getAttr()+ StringConstant.POINT + BusinessConstant.ADDRESS);
                        attrList.add(item.getAttr()+ StringConstant.POINT + BusinessConstant.DISTRICT);
                    }else {
                        attrList.add(item.getAttr());
                    }
                });
                fieldAttrEntities.addAll(newFieldAttrEntities);
                dealAddress(fieldAttrEntities, strings, FieldTypeEnum.COUNT.getAlias(),FieldTypeEnum.COUNT.getDescription(), FieldTypeEnum.COUNT.getType(), null);
                attrList.add(FieldTypeEnum.COUNT.getAlias());
                for (Map.Entry<Integer, HashSet<String>> entry : strings.entrySet()) {
                    HashSet<String> value = entry.getValue();
                    boolean flag = attrList.containsAll(value);
                    if (!flag) {
                        throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403034, MirrorErrorCodeEnum.API_ERROR_403034.getMsg());
                    }
                }

                for (FieldAttrEntity fieldAttrEntity : fieldAttrEntities) {
                    for (Map.Entry<Integer, HashSet<String>> entry : strings.entrySet()) {
                        Integer key = entry.getKey();
                        HashSet<String> value = entry.getValue();
                        if (value.contains(fieldAttrEntity.getAttr())) {
                            if (Objects.equals(fieldAttrEntity.getIsOpen(), BasicConstant.ZERO)) {
                                throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403036, MirrorErrorCodeEnum.API_ERROR_403036.getMsg(), fieldAttrEntity.getAttrName());
                            }
                        }
                    }
                }

            }
            newformIdToAttrs.put(workOrderExplainEntity.getFormId(),fieldAttrEntities);
        }
    }

    /**
     * 处理表单应用和businesstype的关系
     * @param appIds
     * @param formIds
     * @param appBusinessType
     * @param formBusinessType
     * @param formId
     * @param appId
     * @param businessType
     */
    private void dealFormIdsAndAppIds(HashSet<Long> appIds, HashSet<Long> formIds, HashSet<Long> customFormIds, HashMap<Long,HashSet<Integer>> appBusinessType, HashMap<Long,Integer> formBusinessType,HashMap<Integer,HashSet<Long>> businessTypeFormIds, Long formId, Long appId, Integer businessType,Integer saasMark) {
        HashSet<Integer> businessTypeList = new HashSet<>();
        businessTypeList.add(businessType);
        if (Objects.equals(saasMark,SaasMarkEnum.SAAS.getCode())){
            if (Objects.equals(formBusinessType.size(),BasicConstant.ZERO)){
                formBusinessType.put(formId,businessType);
                if (businessTypeFormIds.containsKey(businessType)){
                    HashSet<Long> longs = businessTypeFormIds.get(businessType);
                    longs.add(formId);
                    businessTypeFormIds.put(businessType,longs);
                }else{
                    HashSet<Long> longs = new HashSet<>();
                    longs.add(formId);
                    businessTypeFormIds.put(businessType,longs);
                }
            }
            if (!formBusinessType.containsKey(formId)){
                formBusinessType.put(formId,businessType);
                if (businessTypeFormIds.containsKey(businessType)){
                    HashSet<Long> longs = businessTypeFormIds.get(businessType);
                    longs.add(formId);
                    businessTypeFormIds.put(businessType,longs);
                }else{
                    HashSet<Long> longs = new HashSet<>();
                    longs.add(formId);
                    businessTypeFormIds.put(businessType,longs);
                }
            }
        }else{
            customFormIds.add(formId);
        }

        if (Objects.equals(saasMark,SaasMarkEnum.SAAS.getCode())){
            businessTypeList.add(businessType);
            if (Objects.isNull(appBusinessType.get(appId))){
                appBusinessType.put(appId,businessTypeList);
            }else {
                appBusinessType.get(appId).addAll(businessTypeList);
            }
        }
    }

    private void checkformIdToAttrs(HashMap<Long, Map<Integer, HashSet<String>>> formIdToAttrs,String corpid,HashMap<Long,List<FieldAttrEntity>> newformIdToAttrs) throws XbbException {
        List<Long> formIds = new ArrayList<>();
        Iterator<Long> iterator = formIdToAttrs.keySet().iterator();
        while (iterator.hasNext()) {
            Long formId = iterator.next();
            formIds.add(formId);
        }
        HashMap<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("formIdIn", formIds);
        param.put("corpid", corpid);
        param.put("del", DelEnum.NORMAL.getDel());
        List<PaasFormExplainEntity> entitys = paasFormExplainModel.findEntitys(param);
        List<PaasFormExplainEntity> checkMirror = new ArrayList<>();
        for (PaasFormExplainEntity entity : entitys) {
            List<FieldAttrEntity> fieldAttrEntities = JSON.parseArray(entity.getExplains(), FieldAttrEntity.class);
            HashSet<String> attrList = new HashSet<>();
            Map<Integer, HashSet<String>> strings = formIdToAttrs.get(entity.getFormId());
            if (Objects.nonNull(strings)) {
                List<FieldAttrEntity> newFieldAttrEntities = new ArrayList<>();
                for (FieldAttrEntity item : fieldAttrEntities) {
                    if (ExplainUtil.isSubForm(item.getFieldType())) {
                        SubFormPoJo subForm = item.getSubForm();
                        List<? extends FieldAttrEntity> items = subForm.getItems();
                        for (FieldAttrEntity subForms: items) {
                            String attr = subForms.getAttr();
                            attrList.add(item.getAttr() + "." + attr);
                            attrList.add(item.getAttr());
                            FieldAttrEntity fieldAttrEntity = new FieldAttrEntity();
                            fieldAttrEntity.setAttr(item.getAttr() + "." + attr);
                            fieldAttrEntity.setAttrName(item.getAttrName() + "." +subForms.getAttrName());
                            fieldAttrEntity.setIsRedundant(100);
                            newFieldAttrEntities.add(fieldAttrEntity);
                        }
                    } else if (Objects.equals(item.getFieldType(), FieldTypeEnum.ADDRESS.getType())) {
                        dealAddress(newFieldAttrEntities, strings, item.getAttr(), item.getAttrName(), item.getFieldType(), item);
                        dealAddress(newFieldAttrEntities, strings, item.getAttr() + ".province", I18nMessageUtil.getMessage(AddressEnum.PROVINCE.getName()), item.getFieldType(), item);
                        // address_1.city
                        dealAddress(newFieldAttrEntities, strings, item.getAttr() + ".city", I18nMessageUtil.getMessage(AddressEnum.CITY.getName()), item.getFieldType(), item);
                        //address_1.address
                        dealAddress(newFieldAttrEntities, strings, item.getAttr() + ".address", I18nMessageUtil.getMessage(AddressEnum.ADDRESS.getName()), item.getFieldType(), item);
                        // address_1.district
                        dealAddress(newFieldAttrEntities, strings, item.getAttr() + ".district", I18nMessageUtil.getMessage(AddressEnum.DISTRICT.getName()), item.getFieldType(), item);
                        attrList.add(item.getAttr());
                        attrList.add(item.getAttr()+ StringConstant.POINT + BusinessConstant.PROVINCE);
                        attrList.add(item.getAttr()+ StringConstant.POINT + BusinessConstant.CITY);
                        attrList.add(item.getAttr()+ StringConstant.POINT + BusinessConstant.ADDRESS);
                        attrList.add(item.getAttr()+ StringConstant.POINT + BusinessConstant.DISTRICT);
                    } else {
                        attrList.add(item.getAttr());
                    }
                }
                fieldAttrEntities.addAll(newFieldAttrEntities);
                // // 计数加入
                // HashSet<String> strings = formIdToAttrs.get(entity.getFormId());
                dealAddress(fieldAttrEntities, strings, FieldTypeEnum.COUNT.getAlias(), FieldTypeEnum.COUNT.getDescription(), FieldTypeEnum.COUNT.getType(), null);
                attrList.add(FieldTypeEnum.COUNT.getAlias());
                //图表默认关联的话字段会为-1
                strings.remove("-1");
                //dataId
                dealAddress(fieldAttrEntities, strings, FieldTypeEnum.DATAID.getAlias(), null, FieldTypeEnum.DATAID.getType(), null);

                //
                for (Map.Entry<Integer, HashSet<String>> entry : strings.entrySet()) {
                    HashSet<String> value = entry.getValue();
                    boolean flag = attrList.containsAll(value);
                    if (!flag) {
                        PaasFormEntityExt paasFormEntityExt = paasFormModel.getByKey(entity.getFormId(), entity.getCorpid());
                        throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403027, MirrorErrorCodeEnum.API_ERROR_403027.getMsg(), paasFormEntityExt.getName());
                    }
                }
                newformIdToAttrs.put(entity.getFormId(), fieldAttrEntities);
                Boolean isMirror = false;
                for (FieldAttrEntity fieldAttrEntity : fieldAttrEntities) {
                    for (Map.Entry<Integer, HashSet<String>> entry : strings.entrySet()) {
                        HashSet<String> value = entry.getValue();
                        if (value.contains(fieldAttrEntity.getAttr())) {
                            if (Objects.equals(fieldAttrEntity.getIsOpen(), BasicConstant.ZERO)) {
                                throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403036, MirrorErrorCodeEnum.API_ERROR_403036.getMsg(), fieldAttrEntity.getAttrName());
                            }
                            if (Objects.equals(fieldAttrEntity.getIsRedundant(), BasicConstant.ONE) && Objects.equals(entity.getSaasMark(), SaasMarkEnum.SAAS.getCode())) {
                                isMirror = true;
                                break;
                            }
                        }
                    }

                }
                if (isMirror) {
                    checkMirror.add(entity);
                }

            }
        }
        // 校验系统表单的自定义字段是否在镜像表单里
        if (!Objects.equals(checkMirror.size(), BasicConstant.ZERO)) {
            List<Long> modelFormIds = new ArrayList<>();
            for (PaasFormExplainEntity paasFormExplainEntity : checkMirror) {
                modelFormIds.add(paasFormExplainEntity.getFormId());
                if (Objects.nonNull(FundManagementMenuEnum.getByCode(paasFormExplainEntity.getBusinessType()))){
                    // 关联了资金系统表单的自定义字段
                    throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403037, MirrorErrorCodeEnum.API_ERROR_403037.getMsg(),SystemAppMenuEnum.FUND_MANAGEMENT.getName());
                }
                // 关联了产品系统表达的自定义字段
                if (Objects.nonNull(ProductMenuEnum.getByCode(paasFormExplainEntity.getBusinessType()))){
                    // 关联了资金系统表单的自定义字段
                    throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403037, MirrorErrorCodeEnum.API_ERROR_403037.getMsg(),SystemAppMenuEnum.PRODUCT.getName());
                }
            }
            param.clear();
            param.put("formIdIn", modelFormIds);
            param.put("corpid", corpid);
            param.put("del", DelEnum.NORMAL.getDel());
            List<FormInstallEntity> modelEntitys = formInstallModel.findEntitys(param);
            if (Objects.equals(modelEntitys.size(), BasicConstant.ZERO)) {
                throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403030, MirrorErrorCodeEnum.API_ERROR_403030.getMsg());
            }
        }
    }

    /**
     * 处理地址,dataId,等等字段 count
     * @param fieldAttrEntities
     * @param strings   筛选出来关联的字段
     * @param s 对应的省市地区,count等特殊字段
     * @param attrName     s对应的attrName
     * @param fieldType
     * @param field 主要是给地址使用，数据查询图表，地址不会拆开，需要重新放入，除地址其他 可能为null，谨慎使用
     */
    private void dealAddress(List<FieldAttrEntity> fieldAttrEntities, Map<Integer, HashSet<String>> strings, String s,String attrName, Integer fieldType, FieldAttrEntity field) {
        strings.forEach((key, value)->{
            if (Objects.equals(ChartTypeEnum.DATA_QUERY_TABLE.getCode(), key)) {
                if (!Objects.equals(FieldTypeEnum.ADDRESS.getType(), fieldType) && value.contains(s)) {
                    strings.remove(s);
                    FieldAttrEntity fieldAttrEntity = new FieldAttrEntity();
                    fieldAttrEntity.setAttrName(attrName);
                    // 此处特殊处理: new FieldAttrEntity() 时 isRedundant 为 1, 此处改为其他数字
                    fieldAttrEntity.setIsRedundant(100);
                    fieldAttrEntity.setAttr(s);
                    fieldAttrEntities.add(fieldAttrEntity);
                } else if (Objects.equals(FieldTypeEnum.ADDRESS.getType(), fieldType) && value.contains(s)){
                    fieldAttrEntities.add(field);
                }
            } else {
                if (value.contains(s)) {
                    strings.remove(s);
                    FieldAttrEntity fieldAttrEntity = new FieldAttrEntity();
                    fieldAttrEntity.setAttrName(attrName);
                    // 此处特殊处理: new FieldAttrEntity() 时 isRedundant 为 1, 此处改为其他数字
                    fieldAttrEntity.setIsRedundant(100);
                    fieldAttrEntity.setAttr(s);
                    fieldAttrEntities.add(fieldAttrEntity);
                }
            }
        });


    }

    /**
     * 校验图表对应表单
     * @param formIds   图表关联表单id(源)
     * @param corpid
     * @param formBusinessType
     * @param appInstallIds
     * @param formIdToAttrs
     * @param formIdOldToNewMap
     * @param newForm  后边用到替换源公司解释
     * @throws XbbException
     */
    private void checkForm(HashSet<Long> formIds,String corpid,HashMap<Long,Integer> formBusinessType,HashSet<Long> appInstallIds,HashMap<Long,Map<Integer, HashSet<String>>> formIdToAttrs,
                           HashMap<Long,Long> formIdOldToNewMap,HashMap<Long,String> newForm) throws  XbbException {
        HashMap<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("sourceFormIdIn", formIds);
        param.put("corpid", corpid);
        param.put("orderByStr", "  source_form_id " + SortOrder.DESC + ",  id " + SortOrder.DESC);
        param.put(StringConstant.DEL, DelEnum.NORMAL.getDel());
        List<FormInstallEntity> formInstallEntities = formInstallModel.findEntitys(param);
        HashSet<Long> installFormIds = new HashSet<>();
        HashSet<Long> sourceFormIds = new HashSet<>();
        HashSet<Integer> businessTypes = new HashSet<>();
        //多模板设置
        Integer count = 0;
        HashMap<Integer, Long> businessTypeToFormIds = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (FormInstallEntity formInstallEntity : formInstallEntities) {
            if (!sourceFormIds.contains(formInstallEntity.getSourceFormId())) {
                installFormIds.add(formInstallEntity.getFormId());
                count++;
                formIdOldToNewMap.put(formInstallEntity.getSourceFormId(), formInstallEntity.getFormId());
                sourceFormIds.add(formInstallEntity.getSourceFormId());
            }
        }
        if (!Objects.equals(installFormIds.size(), formIds.size())) {
            for (Long formId : formIds) {
                if (!sourceFormIds.contains(formId)) {
                    if (!Objects.equals(formBusinessType.get(formId), null)) {
                        businessTypes.add(formBusinessType.get(formId));
                    }
                }
            }
            //非镜像安装的系统表单 会去取一遍现有的系统表单,对于多模板会去默认第一条
            // 通过镜像安装的和不是通过镜像安装的都可以
            param.clear();
            param.put("businessTypeList", businessTypes);
            param.put("corpid", corpid);
            param.put("enable",1);
            param.put("del", DelEnum.NORMAL.getDel());
            List<PaasFormEntity> paasFormModelEntitys = paasFormModel.findEntity(param);
            businessTypes.clear();
            paasFormModelEntitys.forEach(item -> {
                if (!businessTypes.contains(item.getBusinessType())) {
                    businessTypes.add(item.getBusinessType());
                    businessTypeToFormIds.put(item.getBusinessType(), item.getId());
                }
            });
            for (Long formId : formIds) {
                if (!sourceFormIds.contains(formId)) {
                    Integer integer = formBusinessType.get(formId);
                    Long aLong = businessTypeToFormIds.get(integer);
                    if (Objects.nonNull(aLong)) {
                        installFormIds.add(aLong);
                        count++;
                        formIdOldToNewMap.put(formId, aLong);
                    }
                }
            }
        }
        param.clear();
        param.put("idIn", installFormIds);
        param.put("corpid", corpid);
        List<PaasFormEntity> paasFormModelEntitys = paasFormModel.findEntity(param);
        //更新formIdToAttrs formid为新安装的formId
        Iterator<Map.Entry<Long, Long>> iterator = formIdOldToNewMap.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<Long, Long> item = iterator.next();
            if (formIdToAttrs.containsKey(item.getKey())) {
                Map<Integer, HashSet<String>> strings = formIdToAttrs.get(item.getKey());
                formIdToAttrs.remove(item.getKey());
                formIdToAttrs.put(formIdOldToNewMap.get(item.getKey()), strings);
            }
        }
        List<Long> newFormIds = new ArrayList<>();
        HashSet<Long> menuIds = new HashSet<>();
        paasFormModelEntitys.forEach(item -> {
            newForm.put(item.getId(), item.getName());
            newFormIds.add(item.getId());
            menuIds.add(item.getMenuId());
        });

        //校验对应的菜单是否关闭
        param.clear();
        param.put("idIn", menuIds);
        param.put("del", DelEnum.NORMAL.getDel());
        param.put("corpid", corpid);
        List<PaasMenuEntity> paasMenuEntityList = paasMenuModel.findEntitys(param);
        for (PaasMenuEntity paasMenuEntity : paasMenuEntityList) {
            if (Objects.equals(paasMenuEntity.getEnable(), BasicConstant.ZERO)) {
                throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403033, MirrorErrorCodeEnum.API_ERROR_403033.getMsg(),paasMenuEntity.getName());
            }
        }

        //校验表单是否存在
        if (!Objects.equals(count, formIds.size())) {
            throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403032, String.format(MirrorErrorCodeEnum.API_ERROR_403032.getMsg(), "").trim());
        }
        for (PaasFormEntity paasFormModelEntity : paasFormModelEntitys) {
            if (Objects.equals(paasFormModelEntity.getEnable(), BasicConstant.ZERO)) {
                throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403025, String.format(MirrorErrorCodeEnum.API_ERROR_403025.getMsg(), paasFormModelEntity.getName()));
            }
            if (Objects.equals(paasFormModelEntity.getDel(), BasicConstant.ONE)) {
                throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403040, String.format(MirrorErrorCodeEnum.API_ERROR_403040.getMsg(), paasFormModelEntity.getName()));
            }
        }
    }

    /**
     * 校验图表关联应用是否安装
     * @param appIds    应用id(源)
     * @param appBusinessType
     * @param corpid
     * @return
     * @throws XbbException
     */
    private HashSet<Long> checkApp(HashSet<Long> appIds, HashMap<Long,HashSet<Integer>> appBusinessType,String corpid ,String sourceCorpid) throws  XbbException {
        HashMap<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("sourceAppIdIn", appIds);
        param.put("corpid", corpid);
        param.put("installType", MirrorTypeEnum.APPLICATION.getCode());
        param.put("del", DelEnum.NORMAL.getDel());
        List<AppInstallEntity> appInstallEntityList = appInstallModel.findEntitys(param);
        HashSet<Long> appInstallIds = new HashSet<>();
        HashSet<Long> sourceAppIds = new HashSet<>();
        appInstallEntityList.forEach(item -> {
            sourceAppIds.add(item.getSourceAppId());
            if (isAddAppInstallIds(item)) {
                appInstallIds.add(item.getId());
            }
        });
        List<Long> customAppId = new ArrayList<>();
        List<String> systemAppAliasIn = new ArrayList<>();
        List<Integer> businessTypes = new ArrayList<>();
        param.clear();
        param.put("idIn", appIds);
        param.put("corpid", sourceCorpid);
        List<PaasAppEntity> appList = paasAppModel.findEntitys(param);
        //app有未安装的(没有安装自定义,系统应用) ,已安装的
        //筛选出来非系统应用和系统应用
        appList.forEach(item -> {
            if (Objects.equals(SaasMarkEnum.PAAS.getCode(), item.getSaasMark())) {
                customAppId.add(item.getId());
            } else{
                systemAppAliasIn.add(item.getAlias());
                if (!appBusinessType.isEmpty()) {
                    businessTypes.addAll(appBusinessType.get(item.getId()));
                }
            }
        });
        // 缺少系统应用
        if (!systemAppAliasIn.isEmpty()) {
            param.clear();
            param.put("aliasIn", systemAppAliasIn);
            param.put("corpid", corpid);
            param.put("del", DelEnum.NORMAL.getDel());
            param.put("enable", 1);
            List<PaasAppEntity> systemAppList = paasAppModel.findEntitys(param);
            // 校验系统应用是否安装
            if (!Objects.equals(systemAppAliasIn.size(), systemAppList.size())) {
                List<String> appName = new ArrayList<>();
                for (PaasAppEntity item : systemAppList) {
                    if (!systemAppAliasIn.contains(item.getAlias())) {
                        appName.add(item.getName());
                    }
                }
                throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403023, String.format(MirrorErrorCodeEnum.API_ERROR_403023.getMsg(), StringUtils.join(appName, "，")));
            }
        }
        //缺少自定义应用
        if (!sourceAppIds.containsAll(customAppId)) {
            //自定义应用未安装
            param.clear();
            customAppId.removeAll(sourceAppIds);
            param.put("idIn",customAppId);
            param.put("corpid",sourceCorpid);
            List<PaasAppEntity> entitys = paasAppModel.findEntitys(param);
            String appName ="";
            for (PaasAppEntity item : entitys) {
                if (Objects.equals(item.getAlias(), XbbRefTypeEnum.PRODUCT.getAlias()) || Objects.equals(item.getAlias(), XbbRefTypeEnum.FUND_MANAGEMENT.getAlias())) {
                    throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403038, String.format(MirrorErrorCodeEnum.API_ERROR_403038.getMsg(), item.getName()));
                }
                appName = appName + "  " + item.getName();
            }
            throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403023, String.format(MirrorErrorCodeEnum.API_ERROR_403023.getMsg(),appName));
        }
        return appInstallIds;
    }

    /**
     * 未回退上一个版本并且可以回退的应用
     * @param item
     * @return
     */
    private boolean isAddAppInstallIds(AppInstallEntity item) {
        return (Objects.equals(item.getIsBack(), MirrorRollBackTypeEnum.OPERATE_BACK.getFlag()) && Objects.equals(item.getIsToPrevious(),MirrorRollBackTypeEnum.NO_ROLL_BACK_TO_PREVIOUS.getFlag())) ||(Objects.equals(item.getIsBack(), MirrorRollBackTypeEnum.NO_OPERATE_BACK_FOR_SYSTEM_APP.getFlag()));
    }

    /**
     *
     * @param chartId
     * @param mirrorChartMap
     * @param formIdToAttrs
     * @param formId
     * @param linkAttrOne
     * @param linkAttrTwo
     * @param saasMark
     */
    private void dealExplains( Long chartId,  Map<Long, MirrorChartEntity> mirrorChartMap, HashMap<Long, Map<Integer,  HashSet<String>>> formIdToAttrs, Long formId,String linkAttrOne,String linkAttrTwo,Integer saasMark) {
        MirrorChartEntity mirrorChartEntity = mirrorChartMap.get(chartId);

        if (Objects.nonNull(formId)){
            if(formIdToAttrs.containsKey(formId)){
                if (formIdToAttrs.get(formId).containsKey(mirrorChartEntity.getChartType())) {
                    HashSet<String> explainToForm = formIdToAttrs.get(formId).get(mirrorChartEntity.getChartType());
                    if (Objects.nonNull(linkAttrOne)) {
                        explainToForm.add(linkAttrOne);
                        formIdToAttrs.get(formId).put(mirrorChartEntity.getChartType(), explainToForm);
                    }
                    if (Objects.nonNull(linkAttrTwo)) {
                        explainToForm.add(linkAttrTwo);
                        formIdToAttrs.get(formId).put(mirrorChartEntity.getChartType(), explainToForm);
                    }
                } else {
                    HashSet<String> explainToForm = new HashSet<>();
                    if (Objects.nonNull(linkAttrOne)) {
                        Map<Integer, HashSet<String>> map = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                        explainToForm.add(linkAttrOne);
                        map.put(mirrorChartEntity.getChartType(), explainToForm);
                        formIdToAttrs.put(formId, map);
                    }
                    if (Objects.nonNull(linkAttrTwo)) {
                        Map<Integer, HashSet<String>> map = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                        explainToForm.add(linkAttrTwo);
                        map.put(mirrorChartEntity.getChartType(), explainToForm);
                        formIdToAttrs.put(formId, map);
                    }
                }
            }else{
                if (Objects.nonNull(linkAttrOne)) {
                    HashSet<String> explainToForm = new HashSet<>();
                    explainToForm.add(linkAttrOne);
                    Map<Integer, HashSet<String>> map = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    map.put(mirrorChartEntity.getChartType(), explainToForm);
                    formIdToAttrs.put(formId, map);
                }
                if (Objects.nonNull(linkAttrTwo)) {
                    HashSet<String> explainToForm = new HashSet<>();
                    explainToForm.add(linkAttrTwo);
                    Map<Integer, HashSet<String>> map = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    map.put(mirrorChartEntity.getChartType(), explainToForm);
                    formIdToAttrs.put(formId, map);
                }
            }
        }
    }

    /**
     * 安装工单镜像
     * @throws XbbException
     * @author xingxing.xiao
     */
    public Long installWorkOrderMirror(String corpid, Long mirrorId, String sourceCorpid, String creatorId, String creatorName, MirrorAppEntity mirrorAppEntity) throws XbbException {
        Long appInstallId = 0L;
        try {
            long now = DateUtil.getInt();
            // 查询该公司工单应用信息
            PaasAppEntity paasAppEntity = paasAppModel.getByAlias(XbbRefTypeEnum.WORK_ORDER.getAlias(), corpid);
            if (Objects.isNull(paasAppEntity)) {
                // 工单若没开启，要提示先开启
                throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403009, MirrorErrorCodeEnum.API_ERROR_403009.getMsg(), SystemAppMenuEnum.WORK_ORDER.getName());
            }
            Long appId = paasAppEntity.getId();

            // 镜像与原表单对应关系(安装时用)
            Map<Long, Long> mirrorFormMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            // 原表单与镜像对应关系(处理解释时用)
            Map<Long, Long> sourceFormMirrorMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);

            // 查询工单菜单（工单菜单只有一个）
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("columns", "id, name");
            param.put("corpid", corpid);
            param.put("appId", appId);
            param.put("del", DelEnum.NORMAL.getDel());
            List<PaasMenuEntity> paasMenuList = paasMenuModel.findEntitys(param);
            Long menuId = paasMenuList.get(0).getId();

            // 镜像工单分类
            param.clear();
            param.put("mirrorId", mirrorId);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorWorkOrderTemplateCategoryEntity> mirrorCategoryList = mirrorWorkOrderTemplateCategoryModel.findEntitys(param);
            // 镜像工单模板
            param.clear();
            param.put("mirrorId", mirrorId);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorWorkOrderTemplateEntity> mirrorTemplateList = mirrorWorkOrderTemplateModel.findEntitys(param);

            // 镜像工单模板和表单关联关系
            List<Long> templateIdIn = new ArrayList<>();
            mirrorTemplateList.forEach(item -> templateIdIn.add(item.getId()));
            param.clear();
            param.put("type", FormBindTemplateTypeEnum.MIRROR.getType());
            param.put("templateIdIn", templateIdIn);
            param.put("del", DelEnum.NORMAL.getDel());
            List<FormBindTemplateInfoEntity> bindEntityList = formBindTemplateInfoModel.findEntitys(param);
            // 镜像工单表单
            param.clear();
            param.put("mirrorId", mirrorId);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorFormEntity> mirrorFormList = mirrorFormModel.findEntitys(param);
            for (MirrorFormEntity entity : mirrorFormList) {
                mirrorFormMap.put(entity.getId(), entity.getSourceFormId());
                sourceFormMirrorMap.put(entity.getSourceFormId(), entity.getId());
            }
            // 工单表单解释
            param.clear();
            param.put("mirrorId", mirrorId);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorFormExplainEntity> mirrorFormExplainList = mirrorFormExplainModel.findEntitys(param);
            // 工单打印模板
            param.clear();
            param.put("mirrorId", mirrorId);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorPrintTemplateEntity> mirrorPrintTemplateList = mirrorPrintTemplateModel.findEntitys(param);

            // 标签处理
            param.clear();
            param.put("mirrorId", mirrorId);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorLabelGroupEntity> mirrorLabelGroupEntities = mirrorLabelGroupModel.findEntitys(param);
            param.clear();
            param.put("mirrorId", mirrorId);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorLabelEntity> mirrorLabelEntities = mirrorLabelModel.findEntitys(param);

            // 低代码
            param.clear();
            param.put("mirrorId", mirrorId);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorLowCodeEntity> mirrorLowCodeEntityList = mirrorLowCodeModel.findEntities(param);

            // 校验自定义表单及字段解释是否存在，还有默认系统表单和默认工单表单信息(找businessType对应关系，后面处理解释中关联字段时会用上)
            HashMap<Long, PaasFormEntity> sourceToNewFormIdMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            HashMap<Integer, PaasFormEntity> businessTypeToNewFormMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            HashMap<Integer, WorkOrderFormEntity> workOrderDefaultToNewFormMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            checkPaasFormField(corpid, mirrorFormExplainList, sourceFormMirrorMap, sourceCorpid, sourceToNewFormIdMap, businessTypeToNewFormMap, workOrderDefaultToNewFormMap);

            // 处理默认分类只有一个(覆盖，也要记录回退状态)
            MirrorWorkOrderTemplateCategoryEntity mirrorDefaultCategoryEntity = null;
            List<MirrorWorkOrderTemplateCategoryEntity>  otherMirrorCategoryList = new ArrayList<>();
            Set<String> mirrorCategoryNames = new HashSet<>();
            boolean hasDefault = false;
            for (MirrorWorkOrderTemplateCategoryEntity entity : mirrorCategoryList) {
                if (Objects.equals(entity.getIsDefault(), 1)) {
                    mirrorDefaultCategoryEntity = entity;
                    hasDefault = true;
                } else {
                    otherMirrorCategoryList.add(entity);
                }
                mirrorCategoryNames.add(entity.getName());
            }
            // 判断分类名称是否已存在
            checkWorkOrderCategoryNames(mirrorCategoryNames, corpid, appId, hasDefault);

            // 处理分类下的模板
            List<MirrorWorkOrderTemplateEntity> mirrorDefaultTemplateList = new ArrayList<>();
            List<MirrorWorkOrderTemplateEntity> mirrorOtherTemplateList = new ArrayList<>();
            List<Long> mirrorDefaultTemaplteIdIn = new ArrayList<>();
            for (MirrorWorkOrderTemplateEntity entity : mirrorTemplateList) {
                if (Objects.nonNull(mirrorDefaultCategoryEntity) && Objects.equals(entity.getCategoryId(), mirrorDefaultCategoryEntity.getId())) {
                    mirrorDefaultTemplateList.add(entity);
                    mirrorDefaultTemaplteIdIn.add(entity.getId());
                } else {
                    mirrorOtherTemplateList.add(entity);
                }
            }
            // 镜像工单模板与表单关联关系
            List<FormBindTemplateInfoEntity> mirrorBindFormList = new ArrayList<>();
            List<FormBindTemplateInfoEntity> mirrorOtherbindFormList = new ArrayList<>();
            Map<Long, Long> mirrorbindFormMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            Map<Long, Long> mirrorDefaultBindFormMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            for (FormBindTemplateInfoEntity entity : bindEntityList) {
                if (mirrorDefaultTemaplteIdIn.contains(entity.getTemplateId())) {
                    mirrorBindFormList.add(entity);
                    mirrorDefaultBindFormMap.put(entity.getFormId(), entity.getTemplateId());
                } else {
                    mirrorOtherbindFormList.add(entity);
                }
                mirrorbindFormMap.put(entity.getFormId(), entity.getTemplateId());
            }
            // 模板对应的表单(通过关联关系找)
            List<MirrorFormEntity> mirrorDefaultFormList = new ArrayList<>();
            List<MirrorFormEntity> mirrorOtherFormList = new ArrayList<>();
            for (MirrorFormEntity entity : mirrorFormList) {
                if (mirrorDefaultBindFormMap.containsKey(entity.getId())) {
                    entity.setMirrorTemplateId(mirrorDefaultBindFormMap.get(entity.getId()));
                    mirrorDefaultFormList.add(entity);
                } else {
                    mirrorOtherFormList.add(entity);
                }
            }

            // 安装之后的工单表单
            List<WorkOrderFormEntity> installFormList = new ArrayList<>();
            // 备份应用id
            Long retainAppId = 0L;
            if (Objects.nonNull(mirrorDefaultCategoryEntity)) {
                // 处理系统默认工单分类(判断是否有数据、覆盖；可以在此处记录回退状态)
                retainAppId = dealWithWorkOrderCover(mirrorDefaultCategoryEntity, mirrorDefaultTemplateList, mirrorDefaultFormList, corpid, appId, menuId, paasAppEntity, creatorId, creatorName, installFormList);
            }

            List<WorkOrderFormEntity> formList = new ArrayList<>();
            // 1. 复制镜像工单分类（除了默认分类）
            List<WorkOrderTemplateCategoryEntity> categoryList = new ArrayList<>();
            Integer categoryMaxSort = getCategoryMaxSort(corpid);
            for (MirrorWorkOrderTemplateCategoryEntity entity : otherMirrorCategoryList) {
                WorkOrderTemplateCategoryEntity categoryEntity = new WorkOrderTemplateCategoryEntity(corpid, appId, entity.getName(), entity.getIsDefault(), entity.getSort(), creatorId, creatorName, creatorId, creatorName, now, now, entity.getDel());
                categoryMaxSort++;
                categoryEntity.setSort(categoryMaxSort);
                categoryEntity.setMirrorCategoryId(entity.getId());
                categoryList.add(categoryEntity);
            }
            if (!categoryList.isEmpty()) {
                workOrderTemplateCategoryModel.insertBatch(categoryList);
                // 2. 复制镜像工单模板
                Map<Long, Long> categoryMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                categoryList.forEach(item -> categoryMap.put(item.getMirrorCategoryId(), item.getId()));
                List<WorkOrderTemplateEntity> templateList = new ArrayList<>();
                mirrorOtherTemplateList.forEach(item -> {
                    Long categoryId = categoryMap.get(item.getCategoryId());
                    // 因为工单不复制流程，所以统一改为自由指派(即：isFree=1), 自由指派的人员也设为空
                    String cc = new JSONArray().toJSONString();
                    String scopeRule = item.getScopeRule();
                    JSONObject scopeRuleObj =  JSONObject.parseObject(scopeRule);
                    scopeRuleObj.put(OptionalRangeEnum.USER.getValue(), new JSONArray().toJSONString());
                    scopeRuleObj.put(OptionalRangeEnum.DEPT.getValue(), new JSONArray().toJSONString());
                    WorkOrderTemplateEntity templateEntity = new WorkOrderTemplateEntity(corpid, appId, categoryId, item.getName(), item.getIntroduction(), 1, cc, item.getEnable(), item.getIsDefault(), scopeRuleObj.toJSONString(), creatorId, creatorName, creatorId, creatorName, now, now, item.getDel(), item.getExpectedTime());
                    templateEntity.setMirrorTemplateId(item.getId());
                    templateList.add(templateEntity);
                });
                if (!templateList.isEmpty()) {
                    workOrderTemplateModel.insertBatch(templateList);
                }
                Map<Long, Long> templateMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                templateList.forEach(item -> templateMap.put(item.getMirrorTemplateId(), item.getId()));
                Map<Long, Long> bindMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                mirrorOtherbindFormList.forEach(item -> {
                    Long templateId = templateMap.get(item.getTemplateId());
                    bindMap.put(item.getFormId(), templateId);
                });

                // 3. 复制工单表单
                mirrorOtherFormList.forEach(item -> {
                    // 处理关联列表(不复制，为 [], 在 form 表的association_list)
                    String associationList = new JSONArray().toJSONString();
                    String businessRules = new JSONArray().toJSONString();
                    Long templateId = bindMap.get(item.getId());
                    // 不复制流程，所以不开启流程表单 isProcessForm=0
                    WorkOrderFormEntity formEntity = new WorkOrderFormEntity(corpid, appId, menuId, item.getName(), item.getEnable(), 0, item.getVerifyRule(), item.getDisableFieldAssign(), item.getFrontCache(), item.getTitleType(), item.getCustomTitle(), associationList, businessRules, item.getSummary(), item.getLabels(), item.getCoOpPerms(), creatorId, now, now, item.getDel(), templateId);
                    formEntity.setMirrorFormId(item.getId());
                    formEntity.setMirrorMenuId(item.getMirrorMenuId());
                    formList.add(formEntity);
                });
                if (!formList.isEmpty()) {
                    workOrderFormModel.insertBatch(formList);
                }
            }

            // 把安装的系统工单表单加上
            if (!installFormList.isEmpty()) {
                formList.addAll(installFormList);
            }

            // 4. 复制工单表单解释
            Map<Long, Long> formMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            formList.forEach(item -> formMap.put(item.getMirrorFormId(), item.getId()));
            List<WorkOrderExplainEntity> explainList = new ArrayList<>();
            // 为了共用处理表单解释的方法，加两个参数
            Map<Long, Long> paasMenuMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            Map<Long, Long> sourceMenuMirrorMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            paasMenuMap.put(0L, menuId);
            for (MirrorFormExplainEntity item : mirrorFormExplainList) {
                Long formId = formMap.get(item.getMirrorFormId());
                WorkOrderExplainEntity explainEntity = new WorkOrderExplainEntity(corpid, appId, menuId, formId, item.getExplains(), creatorId, now, now, item.getDel());
                // 处理字段解释
                List<FieldAttrEntity> fieldExplainList =  JSONArray.parseArray(explainEntity.getExplains(), FieldAttrEntity.class);
                String explains = dealWithExplains(fieldExplainList, appId, paasMenuMap, formMap, corpid, sourceMenuMirrorMap, sourceFormMirrorMap, sourceToNewFormIdMap, businessTypeToNewFormMap, workOrderDefaultToNewFormMap, true);
                explainEntity.setExplains(explains);
                explainList.add(explainEntity);
            }
            if (!explainList.isEmpty()) {
                workOrderExplainModel.insertBatch(explainList);
            }
            // 5. 复制打印模板(系统 + 自定义)
            List<PaasPrintTemplateEntity> printTemplateList = new ArrayList<>();
            for (MirrorPrintTemplateEntity entity : mirrorPrintTemplateList) {
                Long formId = formMap.get(entity.getMirrorFormId());
                PaasPrintTemplateEntity printTemplateEntity = new PaasPrintTemplateEntity(corpid, appId, menuId, formId, entity.getName(), entity.getEnable(), entity.getHtml(), entity.getConfigInfo(), entity.getBusinessType(), creatorId, now, now, entity.getDel());
                printTemplateList.add(printTemplateEntity);
            }
            if (!printTemplateList.isEmpty()) {
                    paasPrintTemplateDao.insertBatch(printTemplateList);
            }
            // 6.复制标签分组
            if (mirrorLabelGroupEntities.size()>0) {
                List<LabelGroupEntity> labelGroupEntities = new ArrayList<>();
                for (MirrorLabelGroupEntity entity : mirrorLabelGroupEntities) {
                    Long formId = formMap.get(entity.getMirrorFormId());
                    LabelGroupEntity labelGroupEntity = new LabelGroupEntity();
                    labelGroupEntity.setCorpid(corpid);
                    labelGroupEntity.setAppId(appId);
                    labelGroupEntity.setFormId(formId);
                    labelGroupEntity.setBusinessType(entity.getBusinessType());
                    labelGroupEntity.setGroupName(entity.getGroupName());
                    labelGroupEntity.setCreatorId(creatorId);
                    labelGroupEntity.setSort(entity.getSort());
                    labelGroupEntity.setAddTime(now);
                    labelGroupEntity.setUpdateTime(now);
                    labelGroupEntity.setMirrorId(entity.getMirrorId());
                    labelGroupEntity.setMirrorGroupId(entity.getSourceGroupId());
                    labelGroupEntity.setDel(DelEnum.NORMAL.getDel());
                    labelGroupEntities.add(labelGroupEntity);
                }
                if (!labelGroupEntities.isEmpty()) {
                    labelGroupModel.insertBatch(labelGroupEntities, corpid);
                }
                Map<Long, Long> sourceGroupId2New = new HashMap<>(labelGroupEntities.size());
                labelGroupEntities.forEach(item->{
                    sourceGroupId2New.put(item.getMirrorGroupId(), item.getId());
                });
                // 7.复制标签
                if (mirrorLabelEntities.size()>0){
                    List<LabelEntity> labelEntitys = new ArrayList<>();
                    for (MirrorLabelEntity entity : mirrorLabelEntities) {
                        Long formId = formMap.get(entity.getMirrorFormId());
                        LabelEntity labelEntity = new LabelEntity();
                        labelEntity.setCorpid(corpid);
                        labelEntity.setAppId(appId);
                        labelEntity.setFormId(formId);
                        labelEntity.setBusinessType(entity.getBusinessType());
                        labelEntity.setGroupId(sourceGroupId2New.get(entity.getSourceGroupId()));
                        labelEntity.setCreatorId(creatorId);
                        labelEntity.setSort(entity.getSort());
                        labelEntity.setName(entity.getName());
                        labelEntity.setEnable(entity.getEnable());
                        labelEntity.setColor(entity.getColor());

                        labelEntity.setAddTime(now);
                        // labelEntity.setEnable(0);
                        labelEntity.setUpdateTime(now);
                        labelEntity.setDel(DelEnum.NORMAL.getDel());
                        labelEntitys.add(labelEntity);
                    }
                    if (!labelGroupEntities.isEmpty()) {
                        labelModel.insertBatch(labelEntitys, corpid);
                    }
                }
            }

            // 安装低代码
            installLowCode(corpid, creatorId, appId, formMap, mirrorLowCodeEntityList, XbbRefTypeEnum.WORK_ORDER.getCode());

            // ========== 记录镜像应用安装信息 =========

            // 更新同一个应用下的前一个工单镜像的操作状态 (不可操作回退)
            updatePreviewInstall(corpid, appId, MirrorApplicationTypeEnum.WORK_ORDER.getCode());

            param.clear();
            param.put("mirrorId", mirrorId);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorMenuEntity> mirrorMenuList = mirrorMenuModel.findEntitys(param);
            Map<Long, Long> mirrorMenuMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            mirrorMenuList.forEach(item -> mirrorMenuMap.put(item.getId(), item.getSourceMenuId()));

            Long sourceAppId = mirrorAppEntity.getSourceAppId();
            AppInstallEntity installEntity = new AppInstallEntity(corpid, AppInstallTypeEnum.MIRROR.getType(), appId, sourceAppId, sourceCorpid, creatorId, mirrorId, MirrorTypeEnum.APPLICATION.getCode(), MirrorApplicationTypeEnum.WORK_ORDER.getCode(), MirrorRollBackTypeEnum.NO_ROLL_BACK_TO_INIT.getFlag(), MirrorRollBackTypeEnum.NO_ROLL_BACK_TO_PREVIOUS.getFlag());
            installEntity.setIsBack(MirrorRollBackTypeEnum.OPERATE_BACK.getFlag());
            if (Objects.equals(retainAppId, 0L)) {
                installEntity.setIsBack(MirrorRollBackTypeEnum.NO_OPERATE_BACK_FOR_SYSTEM_APP.getFlag());
            }
            JSONArray formIds = new JSONArray();
            JSONArray sourceFormIds = new JSONArray();
            // 记录工单表单安装信息
            if (!formList.isEmpty()) {
                List<FormInstallEntity> forminstallList = new ArrayList<>();
                formList.forEach(item -> {
                    Long formId = item.getId();
                    Long sourceFormId = mirrorFormMap.get(item.getMirrorFormId());
                    formIds.add(formId);
                    sourceFormIds.add(sourceFormId);
                    Long sourceMenuId = mirrorMenuMap.get(item.getMirrorMenuId());
                    FormInstallEntity entity = new FormInstallEntity(corpid, AppInstallTypeEnum.MIRROR.getType(), mirrorId, null, appId, menuId, formId, sourceAppId, sourceMenuId, sourceFormId, sourceCorpid, XbbRefTypeEnum.WORK_ORDER.getCode(), item.getDel());
                    entity.setCreatorId(creatorId);
                    entity.setAddTime(now);
                    entity.setUpdateTime(now);
                    forminstallList.add(entity);
                });
                installEntity.setFormIds(formIds.toJSONString());
                installEntity.setSourceFormIds(sourceFormIds.toJSONString());
                appInstallModel.insert(installEntity);
                appInstallId = installEntity.getId();
                forminstallList.forEach(item -> item.setAppInstallId(installEntity.getId()));
                formInstallModel.insertBatch(forminstallList);

                // 更新应用备份表的appInstallId
                if (retainAppId > 0) {
                    PaasAppRetainEntity appRetainEntity = new PaasAppRetainEntity();
                    appRetainEntity.setId(retainAppId);
                    appRetainEntity.setAppInstallId(appInstallId);
                    appRetainEntity.setCorpid(corpid);
                    paasAppRetainModel.update(appRetainEntity);
                }
            }
        } catch (XbbException e){
            throw e;
        } catch (Exception e) {
            LOG.error("installWorkOrderMirror error: ", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        return appInstallId;
    }

    /**
     * 判断分类名称是否已存在
     *
     * 1. 镜像无默认分类，镜像全部分类名称 对比 工单全部分类
     * 2. 镜像有默认分类，镜像全部分类名称 对比 除默认分类外的所有分类（还有一种 镜像其他分类与要覆盖的工单默认相同，暂不考虑吧）
     * @param hasDefault true 有默认分类
     * @author xingxing.xiao
     * @date 2020/04/22 15:06
     */
    private void checkWorkOrderCategoryNames (Set<String> mirrorCategoryNames, String corpid, Long appId, boolean hasDefault) throws XbbException {

        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("columns", "id, name, is_default");
        param.put("corpid", corpid);
        param.put("appId", appId);
        param.put("del", DelEnum.NORMAL.getDel());
        List<WorkOrderTemplateCategoryEntity> categoryList = workOrderTemplateCategoryModel.findEntitys(param);
        for (WorkOrderTemplateCategoryEntity entity : categoryList) {
            if (hasDefault && Objects.equals(entity.getIsDefault(), 1)) {
                continue;
            }
            if (mirrorCategoryNames.contains(entity.getName())) {
                throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403039, MirrorErrorCodeEnum.API_ERROR_403039.getMsg());
            }
        }
    }

    /**
     * 处理系统默认工单分类(判断是否有数据、覆盖；可以在此处记录回退状态)
     * @param mirrorCategoryEntity  默认工单分类
     * @param mirrorTemplateList  默认分类下的模板
     * @param mirrorFormList 模板对应的表单
     * @param installFormList 记录安装之后的工单表单
     * @param corpid
     * @param appId
     * @return retainAppId 应用备份id
     * @author xingxing.xiao
     */
    private Long dealWithWorkOrderCover(MirrorWorkOrderTemplateCategoryEntity mirrorCategoryEntity, List<MirrorWorkOrderTemplateEntity> mirrorTemplateList, List<MirrorFormEntity> mirrorFormList, String corpid, Long appId, Long menuId,PaasAppEntity paasAppEntity,String createId, String createName, List<WorkOrderFormEntity> installFormList) throws XbbException {
        // 查询当前公司的工单默认分类
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("corpid", corpid);
        param.put("appId", appId);
        param.put("isDefault", 1);
        param.put("del", DelEnum.NORMAL.getDel());
        List<WorkOrderTemplateCategoryEntity> categoryList = workOrderTemplateCategoryModel.findEntitys(param);

        // 工单模板
        param.clear();
        param.put("corpid", corpid);
        param.put("appId", appId);
        param.put("categoryId", categoryList.get(0).getId());
        param.put("del", DelEnum.NORMAL.getDel());
        List<WorkOrderTemplateEntity> templateList = workOrderTemplateModel.findEntity(param);

        // 工单表单
        List<Long> templateIdIn = new ArrayList<>();
        for (WorkOrderTemplateEntity entity : templateList) {
            templateIdIn.add(entity.getId());
        }
        param.clear();
        param.put("corpid", corpid);
        param.put("appId", appId);
        param.put("templateIdIn", templateIdIn);
        param.put("del", DelEnum.NORMAL.getDel());
        List<WorkOrderFormEntity> formList = workOrderFormModel.findEntitys(param);

        // ====== 判断工单模板是否有数据 =========
        Set<Long> formIdIn = new HashSet<>();
        Map<Long, Long> formTemplateMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        formList.forEach(item -> {
            formIdIn.add(item.getId());
            formTemplateMap.put(item.getTemplateId(), item.getId());
        });
        boolean dataFlag = checkWorkOrderData(corpid, appId, formIdIn);
        if (dataFlag) {
            throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403003, MirrorErrorCodeEnum.API_ERROR_403003.getMsg(), OperateModuleTypeEnum.WORK_ORDER.getName());
        }

        // 若无数据，覆盖工单分类、工单模板、工单流程(直接删除，因为镜像里没有)、工单表单、工单表单解释
        // 工单流程
        param.clear();
        param.put("corpid", corpid);
        param.put("templateIdIn", templateIdIn);
        param.put("del", DelEnum.NORMAL.getDel());
        List<WorkOrderStageEntity> stageList = workOrderStageModel.findEntitys(param);
        // 工单表单解释
        param.clear();
        param.put("corpid", corpid);
        param.put("formIdIn", formIdIn);
        param.put("del", DelEnum.NORMAL.getDel());
        List<WorkOrderExplainEntity> explainList = workOrderExplainModel.findEntitys(param);
        // 工单打印模板
        param.clear();
        param.put("corpid", corpid);
        param.put("formIdIn", formIdIn);
        param.put("businessType", XbbRefTypeEnum.WORK_ORDER.getCode());
        param.put("del", DelEnum.NORMAL.getDel());
        List<PaasPrintTemplateEntity> printTemplateEntitys = paasPrintTemplateDao.findEntitys(param);
        //备份工单应用
        PaasAppRetainEntity paasAppRetainEntity = new PaasAppRetainEntity(paasAppEntity.getId(), corpid, paasAppEntity.getName(), paasAppEntity.getIcon(), paasAppEntity.getColor(), paasAppEntity.getSaasMark(), paasAppEntity.getSort(), paasAppEntity.getCreatorId(), paasAppEntity.getAddTime(), paasAppEntity.getUpdateTime(), paasAppEntity.getDel(), paasAppEntity.getAlias(), paasAppEntity.getEnable(), paasAppEntity.getDisplay());
        paasAppRetainModel.insert(paasAppRetainEntity);
        Long retainAppId = paasAppRetainEntity.getId();

        //备份工单分类
        List<WorkOrderTemplateCategoryRetainEntity> workOrderTemplateCategoryRetainEntities = new ArrayList<>();
        categoryList.forEach(item->{
            WorkOrderTemplateCategoryRetainEntity workOrderTemplateCategoryRetainEntity = new WorkOrderTemplateCategoryRetainEntity(item);
            workOrderTemplateCategoryRetainEntity.setRetainAppId(retainAppId);
            workOrderTemplateCategoryRetainEntities.add(workOrderTemplateCategoryRetainEntity);
        });

        //备份新工单分类
        workOrderTemplateCategoryRetainModel.insertBatch(workOrderTemplateCategoryRetainEntities);

        //备份工单模板
        List<WorkOrderTemplateRetainEntity> workOrderTemplateRetainEntityList = new ArrayList<>();
        templateList.forEach(item->{
            WorkOrderTemplateRetainEntity workOrderTemplateRetainEntity = new WorkOrderTemplateRetainEntity(item);
            workOrderTemplateRetainEntity.setRetainAppId(retainAppId);
            workOrderTemplateRetainEntityList.add(workOrderTemplateRetainEntity);
        });

        //更新工单模板备份
        workOrderTemplateRetainModel.insertBatch(workOrderTemplateRetainEntityList);

        // 备份工单低代码
        lowCodeRetaion(corpid, retainAppId, new HashSet<>(formIdIn), XbbRefTypeEnum.WORK_ORDER.getCode());

        //备份工单流程
        List<WorkOrderStageRetainEntity> workOrderStageRetainEntities= new ArrayList<>();
        stageList.forEach(item->{
            WorkOrderStageRetainEntity workOrderStageRetainEntity = new WorkOrderStageRetainEntity(item);
            workOrderStageRetainEntity.setRetainAppId(retainAppId);
            workOrderStageRetainEntities.add(workOrderStageRetainEntity);
        });
        //备份工单流程
        if (!Objects.equals(workOrderStageRetainEntities.size(),0)) {
            workOrderStageRetainModel.insertBatch(workOrderStageRetainEntities);
        }
        //备份工单表单
        long now = DateUtil.getInt();
        List<FormBindTemplateInfoEntity> formBindTemplateInfoEntities = new ArrayList<>();
        List<PaasFormRetainEnity> paasFormRetainEnityList = new ArrayList<>();
        formList.forEach(item->{
            FormBindTemplateInfoEntity formBindTemplateInfoEntity= new FormBindTemplateInfoEntity();
            formBindTemplateInfoEntity.setAppId(appId);
            formBindTemplateInfoEntity.setCorpid(corpid);
            formBindTemplateInfoEntity.setFormId(item.getId());
            formBindTemplateInfoEntity.setTemplateId(item.getTemplateId());
            formBindTemplateInfoEntity.setDel(DelEnum.NORMAL.getDel());
            formBindTemplateInfoEntity.setCreatorId(createId);
            formBindTemplateInfoEntity.setAddTime(now);
            formBindTemplateInfoEntity.setUpdateTime(now);
            formBindTemplateInfoEntity.setType(FormBindTemplateTypeEnum.BACK.getType());
            formBindTemplateInfoEntities.add(formBindTemplateInfoEntity);

            PaasFormRetainEnity paasFormRetainEnity= new PaasFormRetainEnity(item);
            paasFormRetainEnity.setRetainAppId(retainAppId);
            paasFormRetainEnityList.add(paasFormRetainEnity);
        });
        //插入工单表单备份
        paasFormRetainModel.insertBatch(paasFormRetainEnityList,corpid);
        //更新工单表单模板关联表
        formBindTemplateInfoModel.insertBatch(formBindTemplateInfoEntities);

        //备份工单解释
        List<PaasFormExplainRetainEntity> paasFormExplainRetainEntities = new ArrayList<>();
        explainList.forEach(item->{
            PaasFormExplainRetainEntity paasFormExplainRetainEntity = new PaasFormExplainRetainEntity(item);
            paasFormExplainRetainEntity.setRetainAppId(retainAppId);
            paasFormExplainRetainEntities.add(paasFormExplainRetainEntity);
        });
        //插入工单表单解释
        paasFormExplainRetainModel.insertBatch(paasFormExplainRetainEntities);
        // 备份工单模板
        if (!printTemplateEntitys.isEmpty()) {
            List<PaasPrintTemplateRetainEntity> paasPrintTemplateRetainEntities = new ArrayList<>();
            printTemplateEntitys.forEach(item -> {
                PaasPrintTemplateRetainEntity paasPrintTemplateRetainEntity = new PaasPrintTemplateRetainEntity(item);
                paasPrintTemplateRetainEntity.setRetainAppId(retainAppId);
                paasPrintTemplateRetainEntities.add(paasPrintTemplateRetainEntity);
            });
            paasPrintTemplateRetainModel.insertBatch(paasPrintTemplateRetainEntities);
            // 删除打印模板
            List<Long> printTemplateIdIn = new ArrayList<>();
            printTemplateEntitys.forEach(item -> printTemplateIdIn.add(item.getId()));
            paasPrintTemplateDao.deleteBatch(printTemplateIdIn, corpid);
        }


        // 覆盖工单默认分类
        WorkOrderTemplateCategoryEntity category = categoryList.get(0);
        WorkOrderTemplateCategoryEntity categoryEntity = new WorkOrderTemplateCategoryEntity();
        categoryEntity.setId(category.getId());
        categoryEntity.setName(mirrorCategoryEntity.getName());
        categoryEntity.setSort(mirrorCategoryEntity.getSort());
        categoryEntity.setModifierId(createId);
        categoryEntity.setModifierName(createName);
        categoryEntity.setUpdateTime(now);
        categoryEntity.setCorpid(corpid);
        workOrderTemplateCategoryModel.update(categoryEntity);

        // 覆盖工单模板和工单表单(数量相等，小于，大于)
        List<WorkOrderTemplateEntity> workOrderTemplateEntityList = new ArrayList<>();
        List<WorkOrderTemplateEntity> workOrderTemplateAddList = new ArrayList<>();
        List<WorkOrderFormEntity> workOrderFormEntityList = new ArrayList<>();
        List<WorkOrderFormEntity> workOrderFormAddList = new ArrayList<>();
        List<MirrorWorkOrderTemplateEntity> mirrorTemplateAddList = new ArrayList<>();
        List<Long> mirrorTemplateAddIn = new ArrayList<>();
        List<Long> templateIdDels = new ArrayList<>();
        List<Long> formIdDels = new ArrayList<>();
        int mirrorCount = mirrorTemplateList.size();
        int count = templateList.size();
        if (mirrorCount == count) {
            for (int i = 0; i < mirrorTemplateList.size(); i++) {
                // 一一对应
                MirrorWorkOrderTemplateEntity mirrorTemplateEntity = mirrorTemplateList.get(i);
                WorkOrderTemplateEntity templateEntity = templateList.get(i);
                setWorkOrderTemplate(mirrorTemplateEntity, templateEntity, createId, createName, now);
                workOrderTemplateEntityList.add(templateEntity);
            }
        } else if (mirrorCount < count) {
            // 小于系统模板数量时，覆盖部分，多余的删除
            for (int i = 0; i < templateList.size(); i++) {
                WorkOrderTemplateEntity templateEntity = templateList.get(i);
                if (i < mirrorCount) {
                    MirrorWorkOrderTemplateEntity mirrorTemplateEntity = mirrorTemplateList.get(i);
                    setWorkOrderTemplate(mirrorTemplateEntity, templateEntity, createId, createName, now);
                    workOrderTemplateEntityList.add(templateEntity);
                } else {
                    templateIdDels.add(templateEntity.getId());
                }
            }
        } else {
            // 大于系统模板时，覆盖全部，多余的新增
            for (int i = 0; i < mirrorTemplateList.size(); i++) {
                MirrorWorkOrderTemplateEntity mirrorTemplateEntity = mirrorTemplateList.get(i);
                if (i < count) {
                    WorkOrderTemplateEntity templateEntity = templateList.get(i);
                    setWorkOrderTemplate(mirrorTemplateEntity, templateEntity, createId, createName, now);
                    workOrderTemplateEntityList.add(templateEntity);
                } else {
                    // categoryId都是一样的，任取一个用
                    Long categoryId = templateList.get(0).getCategoryId();
                    String cc = new JSONArray().toJSONString();
                    String scopeRule = mirrorTemplateEntity.getScopeRule();
                    JSONObject scopeRuleObj =  JSONObject.parseObject(scopeRule);
                    scopeRuleObj.put(OptionalRangeEnum.USER.getValue(), new JSONArray().toJSONString());
                    scopeRuleObj.put(OptionalRangeEnum.DEPT.getValue(), new JSONArray().toJSONString());
                    WorkOrderTemplateEntity templateEntity = new WorkOrderTemplateEntity(corpid, appId, categoryId, mirrorTemplateEntity.getName(), mirrorTemplateEntity.getIntroduction(), 1, cc, mirrorTemplateEntity.getEnable(), mirrorTemplateEntity.getIsDefault(), scopeRuleObj.toJSONString(), createId, createName, createId, createName, now, now, mirrorTemplateEntity.getDel(), mirrorTemplateEntity.getExpectedTime());
                    templateEntity.setMirrorTemplateId(mirrorTemplateEntity.getId());
                    workOrderTemplateAddList.add(templateEntity);
                    // 记录要新增的镜像模板
                    mirrorTemplateAddList.add(mirrorTemplateEntity);
                    mirrorTemplateAddIn.add(mirrorTemplateEntity.getId());
                }
            }
        }
        if (!workOrderTemplateEntityList.isEmpty()) {
            workOrderTemplateModel.updateBatch(workOrderTemplateEntityList, corpid);
            // 连带更新模板下的表单 （找出镜像表单和工单表单做处理）
            Map<Long, MirrorFormEntity> workOrderFormMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            Map<Long, Long> mirrortemplateMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            workOrderTemplateEntityList.forEach(item -> {
                mirrortemplateMap.put(item.getMirrorTemplateId(), item.getId());
            });
            mirrorFormList.forEach(item -> {
                if (mirrortemplateMap.containsKey(item.getMirrorTemplateId())) {
                    Long templateId = mirrortemplateMap.get(item.getMirrorTemplateId());
                    Long formId = formTemplateMap.get(templateId);
                    workOrderFormMap.put(formId, item);
                }
            });
            formList.forEach(item -> {
                if (workOrderFormMap.containsKey(item.getId())) {
                    MirrorFormEntity mirrorFormEntity = workOrderFormMap.get(item.getId());
                    setWorkOrderForm(mirrorFormEntity, item, now);
                    // mirrorFormId 和 mirrorMenuId 后面新增解释找对应关系用
                    item.setMirrorFormId(mirrorFormEntity.getId());
                    item.setMirrorMenuId(mirrorFormEntity.getMirrorMenuId());
                    workOrderFormEntityList.add(item);
                }
            });
            if (!workOrderFormEntityList.isEmpty()) {
                workOrderFormModel.updateBatch(workOrderFormEntityList, corpid);
                for (WorkOrderFormEntity entity : workOrderFormEntityList) {
                    installFormList.add(entity);
                }
            }
        }
        if (templateIdDels.size() > 0) {
            workOrderTemplateModel.deleteBatch(templateIdDels, corpid);
            // 连带删除模板下的表单
            templateIdDels.forEach(item -> {
                if (Objects.nonNull(formTemplateMap.get(item))) {
                    formIdDels.add(formTemplateMap.get(item));
                }
            });
            workOrderFormModel.deleteBatch(formIdDels, corpid);
        }
        if (!workOrderTemplateAddList.isEmpty()) {
            workOrderTemplateModel.insertBatch(workOrderTemplateAddList);

            // 连带新增模板下的表单
            Map<Long, Long> mirrortemplateMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            Map<Long, MirrorFormEntity> workOrderFormMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            workOrderTemplateAddList.forEach(item -> {
                mirrortemplateMap.put(item.getMirrorTemplateId(), item.getId());
            });
            mirrorFormList.forEach(item -> {
                if (mirrorTemplateAddIn.contains(item.getMirrorTemplateId())) {
                    Long templateId = mirrortemplateMap.get(item.getMirrorTemplateId());
                    workOrderFormMap.put(templateId, item);
                }
            });
            // 根据刚新增的工单模板新增表单
            workOrderTemplateAddList.forEach(item -> {
                MirrorFormEntity mirrorFormEntity = workOrderFormMap.get(item.getId());
                String associationList = new JSONArray().toJSONString();
                String businessRules = new JSONArray().toJSONString();
                WorkOrderFormEntity formEntity = new WorkOrderFormEntity(corpid, appId, menuId, mirrorFormEntity.getName(), mirrorFormEntity.getEnable(), 0, mirrorFormEntity.getVerifyRule(), mirrorFormEntity.getDisableFieldAssign(), mirrorFormEntity.getFrontCache(), mirrorFormEntity.getTitleType(), mirrorFormEntity.getCustomTitle(), associationList, businessRules, mirrorFormEntity.getSummary(), mirrorFormEntity.getLabels(), mirrorFormEntity.getCoOpPerms(), createId, now, now, item.getDel(), item.getId());
                // mirrorFormId 和 mirrorMenuId 后面新增解释找对应关系用
                formEntity.setMirrorFormId(mirrorFormEntity.getId());
                formEntity.setMirrorMenuId(mirrorFormEntity.getMirrorMenuId());
                workOrderFormAddList.add(formEntity);
            });
            if (!workOrderFormAddList.isEmpty()) {
                workOrderFormModel.insertBatch(workOrderFormAddList);
                for (WorkOrderFormEntity entity : workOrderFormAddList) {
                    installFormList.add(entity);
                }
            }
        }

        // 工单流程(不用覆盖，直接删除吧)
        List<Long> workOrderStageIdIn = new ArrayList<>();
        stageList.forEach(item -> workOrderStageIdIn.add(item.getId()));
        workOrderStageModel.deleteBatch(workOrderStageIdIn, corpid);

        // 工单表单解释直接删除(跟后面的统一新增)
        List<Long> workOrderExplainIdIn = new ArrayList<>();
        explainList.forEach(item -> workOrderExplainIdIn.add(item.getId()));
        workOrderExplainModel.deleteBatch(workOrderExplainIdIn, corpid);

        return retainAppId;
    }

    /**
     * 工单模板实体赋值
     * @param mirrorTemplateEntity
     * @param templateEntity
     * @param creatorId
     * @param createName
     * @param now
     * @author xingxing.xiao
     */
    private void setWorkOrderTemplate(MirrorWorkOrderTemplateEntity mirrorTemplateEntity, WorkOrderTemplateEntity templateEntity, String creatorId, String createName, Long now) {
        templateEntity.setName(mirrorTemplateEntity.getName());
        templateEntity.setIntroduction(mirrorTemplateEntity.getIntroduction());
        // 统一改为自由指派，且去掉设置的人员
        templateEntity.setIsFree(1);
        templateEntity.setCc(new JSONArray().toJSONString());
        templateEntity.setEnable(mirrorTemplateEntity.getEnable());
        templateEntity.setIsDefault(mirrorTemplateEntity.getIsDefault());
        String scopeRule = mirrorTemplateEntity.getScopeRule();
        JSONObject scopeRuleObj =  JSONObject.parseObject(scopeRule);
        scopeRuleObj.put(OptionalRangeEnum.USER.getValue(), new JSONArray().toJSONString());
        scopeRuleObj.put(OptionalRangeEnum.DEPT.getValue(), new JSONArray().toJSONString());
        templateEntity.setScopeRule(scopeRuleObj.toJSONString());
        templateEntity.setModifierId(creatorId);
        templateEntity.setModifierName(createName);
        templateEntity.setUpdateTime(now);
        templateEntity.setMirrorTemplateId(mirrorTemplateEntity.getId());
        templateEntity.setExpectedTime(mirrorTemplateEntity.getExpectedTime());
    }

    /**
     * 工单表单实体赋值
     * @param mirrorFormEntity
     * @param formEntity
     * @param now
     * @author xingxing.xiao
     */
    private void setWorkOrderForm (MirrorFormEntity mirrorFormEntity, WorkOrderFormEntity formEntity, Long now) {
        formEntity.setName(mirrorFormEntity.getName());
        formEntity.setEnable(mirrorFormEntity.getEnable());
        // 不复制流程，所以不开启流程表单 isProcessForm=0
        formEntity.setIsProcessForm(0);
        formEntity.setVerifyRule(mirrorFormEntity.getVerifyRule());
        formEntity.setDisableFieldAssign(mirrorFormEntity.getDisableFieldAssign());
        formEntity.setFrontCache(mirrorFormEntity.getFrontCache());
        formEntity.setTitleType(mirrorFormEntity.getTitleType());
        formEntity.setCustomTitle(mirrorFormEntity.getCustomTitle());
        // 处理关联列表(不复制，为 [])
        formEntity.setAssociationList(new JSONArray().toJSONString());
        // 工单暂不支持业务规则，如果支持要在后面处理下
        formEntity.setBusinessRules("0");
        formEntity.setSummary(mirrorFormEntity.getSummary());
        formEntity.setLabels(mirrorFormEntity.getLabels());
        formEntity.setCoOpPerms(mirrorFormEntity.getCoOpPerms());
        formEntity.setUpdateTime(now);
    }


    /**
     * 判断工单模板是否有数据
     * @param corpid
     * @return  true 有数据  false 没数据
     * @author xingxing.xiao
     */
    private boolean checkWorkOrderData(String corpid, Long appId, Set<Long> formIdIn) {

        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("corpid", corpid);
        param.put("formIdIn", formIdIn);
        param.put("del", DelEnum.NORMAL.getDel());
        Integer count = workOrderModel.getEntitysCount(param);
        if (count > 0) {
            return true;
        }

        // 流程表单数据
        param.clear();
        param.put("corpid", corpid);
        param.put("appId", appId);
        param.put("formIdIn", formIdIn);
        param.put("del", DelEnum.NORMAL.getDel());
        Integer taskCount = paasProcessTaskModel.getEntitysCount(param);
        if (taskCount > 0) {
            return true;
        }

        return false;
    }

    /**
     * 安装系统表单镜像
     * @throws XbbException
     * @author xingxing.xiao
     */
    public Long installSystemMirror(String corpid, Long mirrorId, String sourceCorpid, String creatorId, MirrorAppEntity mirrorAppEntity, MirrorInstallDTO mirrorInstallDTO) throws XbbException {
        Long appInstallId;
        try {
            long now = DateUtil.getInt();
            // 查询出系统应用信息
            PaasAppEntity paasAppEntity = paasAppModel.getByAlias(mirrorAppEntity.getAlias(), corpid);
            if (Objects.isNull(paasAppEntity)) {
                throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403009, String.format(MirrorErrorCodeEnum.API_ERROR_403009.getMsg(), mirrorAppEntity.getName()));
            }
            Long appId = paasAppEntity.getId();

            // 查出镜像信息(后面要把系统和自定义的区分出来，系统的覆盖，自定义的新增)
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("mirrorId", mirrorId);
            param.put("mirrorAppId", mirrorAppEntity.getId());
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorMenuEntity> mirrorMenuList = mirrorMenuModel.findEntitys(param);
            param.clear();
            param.put("mirrorId", mirrorId);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorFormEntity> mirrorFormList = mirrorFormModel.findEntitys(param);
            param.clear();
            param.put("mirrorId", mirrorId);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorBusinessRuleInfoEntity> mirrorRuleList = mirrorBusinessRuleInfoModel.findEntitys(param);
            param.clear();
            param.put("mirrorId", mirrorId);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorFormExplainEntity> mirrorFormExplainList = mirrorFormExplainModel.findEntitys(param);
            param.clear();
            param.put("mirrorId", mirrorId);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorStageProcessEntity> mirrorStageProcessEntityList = mirrorStageProcessModel.findEntitys(param);
            param.clear();
            param.put("mirrorId", mirrorId);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorStageEntity> mirrorStageEntityList = mirrorStageModel.findEntitys(param);
            param.clear();
            param.put("mirrorId", mirrorId);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorStageWorkEntity> mirrorStageWorkEntityList = mirrorStageWorkModel.findEntitys(param);
            param.clear();
            param.put("mirrorId", mirrorId);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorPrintTemplateEntity> mirrorPrintTemplateList = mirrorPrintTemplateModel.findEntitys(param);
            param.clear();
            param.put("mirrorId", mirrorId);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorLabelGroupEntity> mirrorLabelGroupEntities = mirrorLabelGroupModel.findEntitys(param);
            param.clear();
            param.put("mirrorId", mirrorId);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorLabelEntity> mirrorLabelEntities = mirrorLabelModel.findEntitys(param);
            param.clear();
            param.put("mirrorId", mirrorId);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorLowCodeEntity> mirrorLowCodeEntityList = mirrorLowCodeModel.findEntities(param);

            // 记录镜像与原菜单表单对应关系
            Map<Long, Long> mirrorMenuMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            Map<Long, Long> mirrorFormMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            List<String> systemMenuAliasIn = new ArrayList<>();
            // 原菜单表单与镜像对应关系(处理解释时用)
            Map<Long, Long> sourceMenuMirrorMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            Map<Long, Long> sourceFormMirrorMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            // 记录镜像与新菜单表单等对应关系
            Map<Long, Long> paasMenuMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);

            // 获取父子菜单
            List<PaasMenuEntity> parentMenuList = new ArrayList<>();
            List<PaasMenuEntity> childMenuList = new ArrayList<>();
            // 系统菜单 or 自定义系统菜单(系统菜单用 alias 去查)
            List<MirrorMenuEntity> mirrorSystemMenuList = new ArrayList<>();
            mirrorMenuList.forEach(item -> {
                if (Objects.equals(item.getSaasMark(), SaasMarkEnum.SAAS.getCode())) {
                    mirrorSystemMenuList.add(item);
                    systemMenuAliasIn.add(item.getAlias());
                } else if (Objects.equals(item.getSaasMark(), SaasMarkEnum.PAAS.getCode())) {
                    if (Objects.equals(item.getParentId(), 0L)) {
                        setMenuList(item, parentMenuList, corpid, appId, creatorId, now);
                    } else {
                        setMenuList(item, childMenuList, corpid, appId, creatorId, now);
                    }
                }
                mirrorMenuMap.put(item.getId(), item.getSourceMenuId());
                sourceMenuMirrorMap.put(item.getSourceMenuId(), item.getId());
            });

            // 系统表单 or 自定义系统表单
            List<MirrorFormEntity> mirrorSystemFormList = new ArrayList<>();
            List<MirrorFormEntity> mirrorCustomFormList = new ArrayList<>();
            List<Long> mirrorSysFormIds = new ArrayList<>();
            mirrorFormList.forEach(item -> {
                if (Objects.equals(item.getSaasMark(), SaasMarkEnum.SAAS.getCode())) {
                    mirrorSystemFormList.add(item);
                    mirrorSysFormIds.add(item.getId());
                } else if (Objects.equals(item.getSaasMark(), SaasMarkEnum.PAAS.getCode())) {
                    mirrorCustomFormList.add(item);
                }
                mirrorFormMap.put(item.getId(), item.getSourceFormId());
                sourceFormMirrorMap.put(item.getSourceFormId(), item.getId());
            });
            // 安装之后的表单
            List<PaasFormEntity> installFormList = new ArrayList<>();
            // 备份应用id
            Long retainAppId = 0L;

            // 提取业务规则并判断表单及字段
            HashMap<Long, PaasFormEntity> sourceToNewFormMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            if (CollectionsUtil.isNotEmpty(mirrorRuleList)) {
                BusinessRuleWhiteDTO businessRuleWhiteDTO = new BusinessRuleWhiteDTO();
                BeanUtil.copyProperties(mirrorInstallDTO, businessRuleWhiteDTO);
                BusinessRuleWhiteVO businessRuleWhiteVO = validateVersionAndWhite(businessRuleWhiteDTO);
                Integer feeType = businessRuleWhiteVO.getFeeType();
                // 标准版和高级版不能安装业务规则
                if (!Objects.equals(feeType, 3)) {
                    throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403047);
                }
                sourceToNewFormMap = checkBusinessRuleForm(corpid, mirrorRuleList, sourceFormMirrorMap, sourceCorpid);
            }

            // 校验自定义表单及字段解释是否存在，还有默认系统表单和默认工单表单信息(找businessType对应关系，后面处理解释中关联字段时会用上)
            HashMap<Long, PaasFormEntity> sourceToNewFormIdMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            HashMap<Integer, PaasFormEntity> businessTypeToNewFormMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            HashMap<Integer, WorkOrderFormEntity> workOrderDefaultToNewFormMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            checkPaasFormField(corpid, mirrorFormExplainList, sourceFormMirrorMap, sourceCorpid, sourceToNewFormIdMap, businessTypeToNewFormMap, workOrderDefaultToNewFormMap);

            if (!systemMenuAliasIn.isEmpty()) {
                param.clear();
                param.put("corpid", corpid);
                param.put("appId", appId);
                param.put("aliasIn", systemMenuAliasIn);
                param.put("del", DelEnum.NORMAL.getDel());
                List<PaasMenuEntity> paasMenuList = paasMenuModel.findEntitys(param);
                if (!paasMenuList.isEmpty()) {
                    List<Long> paasMenuIdIn = new ArrayList<>();
                    paasMenuList.forEach(item -> paasMenuIdIn.add(item.getId()));
                    param.clear();
                    param.put("corpid", corpid);
                    param.put("menuIdIn", paasMenuIdIn);
                    param.put("del", DelEnum.NORMAL.getDel());
                    List<PaasFormEntity> paasformList = paasFormModel.findEntity(param);
                    // 判断系统表单是否有数据（还有流程表单数据）
                    checkFormData(paasformList, corpid, appId);
                    // 判断阶段推进器是否有数据
                    checkThrusterData(paasformList, corpid);
                    // 系统要覆盖的表单（包含菜单、表单、表单解释、阶段推进器三张表、打印模板,可以在此处记录回退状态）
                    retainAppId = coverForm(mirrorSystemMenuList, mirrorSystemFormList, paasMenuMap, paasMenuList, paasformList, corpid, paasAppEntity, creatorId, installFormList);
                }
            }

            // 处理 sort
            Integer sort = getMenuMaxSort(corpid, appId);
            for (PaasMenuEntity menuEntity : parentMenuList) {
                 sort++;
                 menuEntity.setSort(sort);
            }
            if (!parentMenuList.isEmpty()) {
                paasMenuModel.insertBatch(parentMenuList);
            }
            Map<Long, Long> parentMenuMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            for (PaasMenuEntity parentMenu : parentMenuList) {
                parentMenuMap.put(parentMenu.getMirrorMenuId(), parentMenu.getId());
                paasMenuMap.put(parentMenu.getMirrorMenuId(), parentMenu.getId());
            }
            // 复制子菜单
            for (PaasMenuEntity childMenu : childMenuList) {
                if (Objects.nonNull(parentMenuMap.get(childMenu.getParentId()))) {
                    childMenu.setParentId(parentMenuMap.get(childMenu.getParentId()));
                }
            }
            if (!childMenuList.isEmpty()) {
                paasMenuModel.insertBatch(childMenuList);
                for (PaasMenuEntity childMenu : childMenuList) {
                    paasMenuMap.put(childMenu.getMirrorMenuId(), childMenu.getId());
                }
            }
            // 3. 复制自定义表单
            List<PaasFormEntity> formList = new ArrayList<>();
            for (MirrorFormEntity entity : mirrorCustomFormList) {
                // 不复制流程，所以不开启流程表单(isProcessForm=0)
                Long menuId = paasMenuMap.get(entity.getMirrorMenuId());
                // 处理关联列表(不复制，为 [], 在 form 表的association_list)
                String associationList = new JSONArray().toJSONString();
                String permission = new JSONArray().toJSONString();
                // 默认没有规则，后面若可以复制规则，再更新 businessRule = 1
                String businessRules = "0";
                PaasFormEntity formEntity = new PaasFormEntity(corpid, appId, menuId, entity.getName(), entity.getSaasMark(), entity.getBusinessType(), entity.getEnable(), 0, entity.getVerifyRule(), entity.getDisableFieldAssign(), entity.getFrontCache(), entity.getTitleType(), entity.getCustomTitle(), associationList, businessRules, entity.getSurcharge(), entity.getOpenDistribution(), entity.getPriceAssociation(), entity.getSummary(), entity.getLabels(), entity.getCoOpPerms(), permission, creatorId, now, now, entity.getDel(), entity.getFieldPosition(),entity.getHidden(), entity.getDistributorMark());
                formEntity.setMirrorFormId(entity.getId());
                formEntity.setMirrorAppId(entity.getMirrorAppId());
                formEntity.setMirrorMenuId(entity.getMirrorMenuId());
                formEntity.setSort(0);
                formList.add(formEntity);
            }
            if (!formList.isEmpty()) {
                paasFormModel.insertBatch(formList);
            }
            // 把安装的系统表单也加上
            if (!installFormList.isEmpty()) {
                formList.addAll(installFormList);
            }
            if (!formList.isEmpty()) {
                // 镜像与表单对应关系
                Map<Long, Long> paasFormMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                for (PaasFormEntity entity : formList) {
                    paasFormMap.put(entity.getMirrorFormId(), entity.getId());
                }

                // 处理业务规则
                if (CollectionsUtil.isNotEmpty(mirrorRuleList)) {
                    List<BusinessRuleInfoEntity> businessRuleList = new ArrayList<>();
                    for (MirrorBusinessRuleInfoEntity entity : mirrorRuleList) {
                        Long menuId = paasMenuMap.get(entity.getMirrorMenuId());
                        Long formId = paasFormMap.get(entity.getMirrorFormId());
                        BusinessRuleInfoEntity ruleInfoEntity = new BusinessRuleInfoEntity(corpid, entity.getSaasMark(), entity.getBusinessType(), formId, appId, menuId, entity.getActiveName(), entity.getActiveType(), entity.getActiveSaasMark(), entity.getActiveBusinessType(),
                                entity.getActiveFormId(), entity.getActiveMenuId(), entity.getActiveAppId(), entity.getActiveOperationType(), entity.getActivePreConditions(), entity.getActiveFilterConditions(), entity.getActiveOperations(), entity.getActiveMemo(), entity.getActiveEnable(),
                                entity.getInactiveName(), entity.getInactiveType(), entity.getInactiveSaasMark(), entity.getInactiveBusinessType(), entity.getInactiveFormId(), entity.getInactiveMenuId(), entity.getInactiveAppId(), entity.getInactiveOperationType(), entity.getInactivePreConditions(), entity.getInactiveFilterConditions(), entity.getInactiveOperations(), entity.getInactiveMemo(), entity.getInactiveEnable(), entity.getPriority(), creatorId, now, now, entity.getDel());
                        businessRuleList.add(ruleInfoEntity);
                    }
                    if (!businessRuleList.isEmpty()) {
                        // 处理业务规则-表单等替换成新的
                        dealWithBusinessRules(businessRuleList, appId, paasMenuMap, paasFormMap, corpid, sourceMenuMirrorMap, sourceFormMirrorMap, sourceToNewFormMap, false);
                        businessRuleInfoModel.insertBatch(businessRuleList);

                        // 批量更新表单表的businessRule=1
                        Set<Long> formIdSets = new HashSet<>();
                        businessRuleList.forEach(item -> formIdSets.add(item.getFormId()));
                        List<Long> ruleFormIds = new ArrayList<>(formIdSets);
                        paasFormModel.updateBusinessRuleBatch(ruleFormIds, corpid, "1");
                    }
                }

                // 4. 复制表单解释(系统 + 自定义)
                List<PaasFormExplainEntity> formExplainList = new ArrayList<>();
                for (MirrorFormExplainEntity entity : mirrorFormExplainList) {
                    Long menuId = paasMenuMap.get(entity.getMirrorMenuId());
                    Long formId = paasFormMap.get(entity.getMirrorFormId());
                    PaasFormExplainEntity formExplainEntity = new PaasFormExplainEntity(corpid, appId, menuId, formId, entity.getEnable(), entity.getExplains(), creatorId, now, now, entity.getDel(), entity.getSaasMark(), entity.getBusinessType(), DistributorMarkEnum.OTHER.getCode());
                    // 处理字段解释
                    List<FieldAttrEntity> explainList =  JSONArray.parseArray(formExplainEntity.getExplains(), FieldAttrEntity.class);
                    String explains = dealWithExplains(explainList, appId, paasMenuMap, paasFormMap, corpid, sourceMenuMirrorMap, sourceFormMirrorMap, sourceToNewFormIdMap, businessTypeToNewFormMap, workOrderDefaultToNewFormMap, false);
                    formExplainEntity.setExplains(explains);
                    formExplainList.add(formExplainEntity);
                }
                if (!formExplainList.isEmpty()) {
                    paasFormExplainModel.insertBatch(formExplainList);
                }

                // 对阶段推进器的表单进行安装（注：前面系统表单备份时并没有插入阶段推进器，而是放在和paas表单一起插入）
                StageInstallPojo stageInstallPojo = new StageInstallPojo(corpid, paasFormMap, mirrorStageProcessEntityList, mirrorStageEntityList, mirrorStageWorkEntityList);
                installStageThruster(stageInstallPojo);

                // 5. 复制打印模板(系统 + 自定义)
                List<PaasPrintTemplateEntity> printTemplateList = new ArrayList<>();
                for (MirrorPrintTemplateEntity entity : mirrorPrintTemplateList) {
                    Long menuId = paasMenuMap.get(entity.getMirrorMenuId());
                    Long formId = paasFormMap.get(entity.getMirrorFormId());
                    PaasPrintTemplateEntity printTemplateEntity = new PaasPrintTemplateEntity(corpid, appId, menuId, formId, entity.getName(), entity.getEnable(), entity.getHtml(), entity.getConfigInfo(), entity.getBusinessType(), creatorId, now, now, entity.getDel());
                    printTemplateList.add(printTemplateEntity);
                }
                if (!printTemplateList.isEmpty()) {
                    paasPrintTemplateDao.insertBatch(printTemplateList);
                }
                // 6.复制标签分组
                if (mirrorLabelGroupEntities.size()>0) {
                    List<LabelGroupEntity> labelGroupEntities = new ArrayList<>();
                    for (MirrorLabelGroupEntity entity : mirrorLabelGroupEntities) {
                        Long formId = paasFormMap.get(entity.getMirrorFormId());
                        LabelGroupEntity labelGroupEntity = new LabelGroupEntity();
                        labelGroupEntity.setCorpid(corpid);
                        labelGroupEntity.setAppId(appId);
                        labelGroupEntity.setFormId(formId);
                        labelGroupEntity.setBusinessType(entity.getBusinessType());
                        labelGroupEntity.setGroupName(entity.getGroupName());
                        labelGroupEntity.setCreatorId(creatorId);
                        labelGroupEntity.setSort(entity.getSort());
                        labelGroupEntity.setAddTime(now);
                        labelGroupEntity.setUpdateTime(now);
                        labelGroupEntity.setMirrorId(entity.getMirrorId());
                        labelGroupEntity.setMirrorGroupId(entity.getSourceGroupId());
                        labelGroupEntity.setDel(DelEnum.NORMAL.getDel());
                        labelGroupEntities.add(labelGroupEntity);
                    }
                    if (!labelGroupEntities.isEmpty()) {
                        labelGroupModel.insertBatch(labelGroupEntities, corpid);
                    }
                    Map<Long, Long> sourceGroupId2New = new HashMap<>(labelGroupEntities.size());
                    labelGroupEntities.forEach(item->{
                        sourceGroupId2New.put(item.getMirrorGroupId(), item.getId());
                    });
                    // 7.复制标签
                    if (mirrorLabelEntities.size()>0){
                        List<LabelEntity> labelEntitys = new ArrayList<>();
                        for (MirrorLabelEntity entity : mirrorLabelEntities) {
                            if (!sourceGroupId2New.containsKey(entity.getSourceGroupId())) {
                                // 避免sql报错，导致安装失败
                                continue;
                            }
                            Long formId = paasFormMap.get(entity.getMirrorFormId());
                            LabelEntity labelEntity = new LabelEntity();
                            labelEntity.setCorpid(corpid);
                            labelEntity.setAppId(appId);
                            labelEntity.setFormId(formId);
                            labelEntity.setBusinessType(entity.getBusinessType());
                            labelEntity.setGroupId(sourceGroupId2New.get(entity.getSourceGroupId()));
                            labelEntity.setCreatorId(creatorId);
                            labelEntity.setSort(entity.getSort());
                            labelEntity.setName(entity.getName());
                            labelEntity.setEnable(entity.getEnable());
                            labelEntity.setColor(entity.getColor());

                            labelEntity.setAddTime(now);
                            // labelEntity.setEnable(0);
                            labelEntity.setUpdateTime(now);
                            labelEntity.setDel(DelEnum.NORMAL.getDel());
                            labelEntitys.add(labelEntity);
                        }
                        if (!labelGroupEntities.isEmpty()) {
                            labelModel.insertBatch(labelEntitys, corpid);
                        }
                    }
                }

                // 8.复制低代码
                installLowCode(corpid, creatorId, appId, paasFormMap, mirrorLowCodeEntityList);
            }

            // 记录镜像应用安装信息
            appInstallId = recordAppInstall(formList, paasMenuMap, mirrorMenuMap, mirrorFormMap, corpid, appId, mirrorAppEntity.getSourceAppId(), sourceCorpid, creatorId, mirrorId, MirrorApplicationTypeEnum.SYSTEM.getCode(), retainAppId);

            // 更新应用备份表的appInstallId
            if (retainAppId > 0) {
                PaasAppRetainEntity appRetainEntity = new PaasAppRetainEntity();
                appRetainEntity.setId(retainAppId);
                appRetainEntity.setAppInstallId(appInstallId);
                appRetainEntity.setCorpid(corpid);
                paasAppRetainModel.update(appRetainEntity);
            }
        } catch (XbbException e) {
            throw e;
        } catch (Exception e) {
            LOG.error("installSystemMirror error: ", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        return appInstallId;
    }

    /**
     * 安装低代码-非工单业务调用
     * @param corpid
     * @param userId
     * @param appId
     * @param paasFormMap
     * @param mirrorLowCodeEntityList
     */
    private void installLowCode(String corpid, String userId, Long appId, Map<Long, Long> paasFormMap, List<MirrorLowCodeEntity> mirrorLowCodeEntityList) {
        installLowCode(corpid, userId, appId, paasFormMap, mirrorLowCodeEntityList, null);
    }

    /**
     * 安装低代码
     * @param corpid 公司id
     * @param userId 用户id
     * @param paasFormMap 表单映射
     * @param mirrorLowCodeEntityList 低代码镜像列表
     */
    private void installLowCode(String corpid, String userId, Long appId, Map<Long, Long> paasFormMap, List<MirrorLowCodeEntity> mirrorLowCodeEntityList, Integer businessType) {

        if (CollectionsUtil.isEmpty(mirrorLowCodeEntityList)) {
            return;
        }
        Map<Long, MirrorLowCodeEntity> mirrorLowCodeEntityMap = Maps.newHashMapWithExpectedSize(paasFormMap.size());
        List<LowCodeJsFileEntity> lowCodeJsFileEntityList = new ArrayList<>(mirrorLowCodeEntityList.size());
        for (MirrorLowCodeEntity mirrorLowCodeEntity : mirrorLowCodeEntityList) {
            Long formId = paasFormMap.get(mirrorLowCodeEntity.getMirrorFormId());
            LowCodeJsFileEntity lowCodeJsFileEntity = new LowCodeJsFileEntity();
            lowCodeJsFileEntity.setCorpid(corpid);
            lowCodeJsFileEntity.setFormId(formId);
            lowCodeJsFileEntity.setSaasMark(mirrorLowCodeEntity.getSaasMark());
            lowCodeJsFileEntity.setBusinessType(mirrorLowCodeEntity.getBusinessType());
            lowCodeJsFileEntity.setWhetherToPublish(BasicConstant.ONE);
            lowCodeJsFileEntity.setCreatorId(userId);
            long now = DateTimeUtil.getInt();
            lowCodeJsFileEntity.setUpdateTime(now);
            lowCodeJsFileEntity.setAddTime(now);
            lowCodeJsFileEntity.setDel(DelEnum.NORMAL.getDel());

            //拷贝低代码文件
            String publish = lowCodeHelp.copyLowCodeRepFile(mirrorLowCodeEntity.getPublish(), StringConstant.LOW_CODE_RELEASE_SUFFIX, corpid, appId, formId);
            lowCodeJsFileEntity.setPublish(publish);
            String dev = lowCodeHelp.copyLowCodeRepFile(mirrorLowCodeEntity.getPublish(), StringConstant.LOW_CODE_DEV_SUFFIX, corpid, appId, formId);
            lowCodeJsFileEntity.setUnreleased(dev);

            lowCodeJsFileEntityList.add(lowCodeJsFileEntity);
            mirrorLowCodeEntityMap.put(formId, mirrorLowCodeEntity);
        }
        // 覆盖操作，覆盖原有的低代码
        lowCodeHelp.deleteLowCodeConfig(corpid, paasFormMap.values(), businessType);
        if (CollectionsUtil.isNotEmpty(lowCodeJsFileEntityList)) {
            lowCodeJsFileModel.insertBatch(lowCodeJsFileEntityList);
        }

        //低代码配置
        saveCustomerButton(corpid, userId, mirrorLowCodeEntityMap);
        saveActionSetting(corpid, userId, mirrorLowCodeEntityMap);

    }

    /**
     * 拷贝低代码文件
     * @param jsFilePojoStr
     * @param suffix
     * @return
     */
    public String copyLowCodeRepFile(String jsFilePojoStr, String suffix, String corpid, Long appId, Long formId) {
        try {
            if (Objects.nonNull(jsFilePojoStr) && StringUtil.isNotEmpty(jsFilePojoStr)) {
                JsFilePojo jsFilePojo = JSON.parseObject(jsFilePojoStr, JsFilePojo.class);
                if (Objects.isNull(jsFilePojo) || Objects.isNull(jsFilePojo.getAttachIndex())) {
                    return null;
                }
                // 获取源文件路径
                String host = "https://" + proBaseConfig.getLowCodeBucketName() + StringConstant.POINT + proBaseConfig.getLowCodeEndpointSimple();
                String attachIndex = jsFilePojo.getAttachIndex();
                String fileDir = attachIndex.replace(host, "");
                String sourcePath = fileDir.substring(1);

                // 目标文件名
                String targetFileName = formId + StringConstant.CROSS + suffix;
                String targetPath = ProBaseConfig.getProjectName() + StringConstant.SLASH + corpid + StringConstant.SLASH + appId + StringConstant.SLASH + targetFileName;

                aliyunOssService.copyLowCodeFile(sourcePath, targetPath);
                String targetIndex = host + StringConstant.SLASH + targetPath;
                jsFilePojo.setAttachIndex(targetIndex);
                jsFilePojo.setFilename(targetFileName);
                return JSON.toJSONString(jsFilePojo);
            }
        } catch (Exception e) {
            LOG.error("保存低代码镜像失败! ", e);
        }
        return null;
    }

    /**
     * 保存低代码自定义按钮
     * @param corpid 公司id
     * @param userId 用户id
     * @param mirrorLowCodeEntityMap 镜像map
     */
    private void saveCustomerButton(String corpid, String userId, Map<Long, MirrorLowCodeEntity> mirrorLowCodeEntityMap) {
        if (Objects.isNull(mirrorLowCodeEntityMap) || mirrorLowCodeEntityMap.isEmpty()) {
            return;
        }
        List<CustomButtonEntity> addList = new ArrayList<>();
        List<CustomButtonActionEntity> actionAddList = new ArrayList<>();

        for (Long formId : mirrorLowCodeEntityMap.keySet()) {
            MirrorLowCodeEntity mirrorLowCodeEntity = mirrorLowCodeEntityMap.get(formId);
            String lowCodeConfig = mirrorLowCodeEntity.getLowCodeConfig();
            LowCodeConfigRetaionPojo lowCodeConfigRetaionPojo = JSON.parseObject(lowCodeConfig, LowCodeConfigRetaionPojo.class);
            List<CustomButtonPojo> customButtonPojoList = lowCodeConfigRetaionPojo.getCustomButtonPojoList();
            if (CollectionsUtil.isEmpty(customButtonPojoList)) {
                continue;
            }
            for (CustomButtonPojo customButton : customButtonPojoList) {
                long now = DateTimeUtil.getInt();
                List<CustomButtonActionEntity> actionList = new ArrayList<>();
                CustomButtonEntity customButtonEntity = new CustomButtonEntity(corpid, formId, mirrorLowCodeEntity.getSaasMark(), mirrorLowCodeEntity.getBusinessType(), customButton.getName(), customButton.getIcon(), customButton.getColor(),
                        customButton.getType(), customButton.getSort(), userId, now, now);
                List<CustomButtonActionPojo> customButtonActionList = customButton.getActionList();
                if (CollectionUtils.isNotEmpty(customButtonActionList)) {
                    customButtonActionList.forEach(action -> {
                        Long serviceId = 0L;
                        CustomButtonActionEntity customButtonActionEntity = new CustomButtonActionEntity(corpid, formId, mirrorLowCodeEntity.getSaasMark(), mirrorLowCodeEntity.getBusinessType(), 0L, action.getActionType(), serviceId,
                                JSON.toJSONString(action.getParam()), action.getSort(), userId, now, now);
                        actionList.add(customButtonActionEntity);
                    });
                }
                if (CollectionUtils.isNotEmpty(actionList)) {
                    customButtonEntity.setActionList(actionList);
                    addList.add(customButtonEntity);
                }
            }
        }

        //入库
        if (CollectionsUtil.isNotEmpty(addList)) {
            customButtonModel.insertBatch(addList);
            addList.forEach(item-> item.getActionList().forEach(action -> {
                action.setButtonId(item.getId());
                actionAddList.add(action);
            }));
        }
        if (CollectionsUtil.isNotEmpty(actionAddList)) {
            customButtonActionModel.insertBatch(actionAddList);
        }
    }

    /**
     * 保存低代码动作配置
     * @param corpid 公司id
     * @param userId 用户id
     * @param mirrorLowCodeEntityMap 镜像Map
     */
    private void saveActionSetting(String corpid, String userId, Map<Long, MirrorLowCodeEntity> mirrorLowCodeEntityMap) {
        if (Objects.isNull(mirrorLowCodeEntityMap) || mirrorLowCodeEntityMap.isEmpty()) {
            return;
        }
        List<LowCodeActionSettingEntity> addList = new ArrayList<>();

        for (Long formId : mirrorLowCodeEntityMap.keySet()) {
            MirrorLowCodeEntity mirrorLowCodeEntity = mirrorLowCodeEntityMap.get(formId);
            String lowCodeConfig = mirrorLowCodeEntity.getLowCodeConfig();
            LowCodeConfigRetaionPojo lowCodeConfigRetaionPojo = JSON.parseObject(lowCodeConfig, LowCodeConfigRetaionPojo.class);
            List<LowCodeActionSettingPojo> lowCodeActionSettingList = lowCodeConfigRetaionPojo.getActionSettingList();
            if (CollectionsUtil.isEmpty(lowCodeActionSettingList)) {
                continue;
            }
            lowCodeActionSettingList.forEach(item -> {
                Long serviceId = 0L;
                long now = DateTimeUtil.getInt();
                LowCodeActionSettingEntity lowCodeActionSettingEntity = new LowCodeActionSettingEntity(corpid, formId, mirrorLowCodeEntity.getSaasMark(), mirrorLowCodeEntity.getBusinessType(), item.getType(), item.getTriggerTiming(),
                        item.getActionType(), item.getAttr(), item.getFieldType(), serviceId,
                        JSON.toJSONString(item.getParam()), item.getSort(), userId, now, now);
                addList.add(lowCodeActionSettingEntity);
            });

        }
        if (CollectionsUtil.isNotEmpty(addList)) {
            lowCodeActionSettingModel.addBatch(addList);
        }
    }

    /**
     * 处理业务规则
     * @param businessRuleList
     * @param appId
     * @param paasMenuMap
     * @param paasFormMap
     * @param corpid
     * @param sourceMenuMirrorMap
     * @param sourceFormMirrorMap
     * @param sourceToNewFormMap (安装过的表单) 新旧表单对应关系
     * @param isWorkOrder 是否工单，工单菜单要特殊处理
     * @return 替换好的业务规则
     * @throws
     * @author xingxing.xiao
     * @date 2020/05/14 10:55
     */
    private String dealWithBusinessRules (List<BusinessRuleInfoEntity> businessRuleList, Long appId, Map<Long, Long> paasMenuMap, Map<Long, Long> paasFormMap, String corpid, Map<Long, Long> sourceMenuMirrorMap, Map<Long, Long> sourceFormMirrorMap, HashMap<Long, PaasFormEntity> sourceToNewFormMap, boolean isWorkOrder) throws XbbException {
        for (BusinessRuleInfoEntity ruleInfoEntity : businessRuleList) {
            // 生效规则
            if (StringUtil.isNotEmpty(ruleInfoEntity.getActiveName())) {
                // 高级规则(公式) or 简易规则
                Integer ruleType = ruleInfoEntity.getActiveType();
                // 操作方式
                List<OperationsPojo> operationsPojos = JSONArray.parseArray(ruleInfoEntity.getActiveOperations(), OperationsPojo.class);
                if (sourceFormMirrorMap.containsKey(ruleInfoEntity.getActiveFormId())) {
                    // 同一个镜像里的表单，可直接替换 （对应关系 sourceFormId -> mirrorFormId -> newFormId）

                    // 目标表单
                    ruleInfoEntity.setActiveAppId(appId);
                    if (isWorkOrder) {
                        ruleInfoEntity.setActiveMenuId(paasMenuMap.get(0L));
                    } else {
                        Long mirrorMenuId = sourceMenuMirrorMap.get(ruleInfoEntity.getActiveMenuId());
                        ruleInfoEntity.setActiveMenuId(paasMenuMap.get(mirrorMenuId));
                    }

                    Long sourceActiveFormId = ruleInfoEntity.getActiveFormId();
                    Long mirrorFormId = sourceFormMirrorMap.get(sourceActiveFormId);
                    Long formId = paasFormMap.get(mirrorFormId);
                    ruleInfoEntity.setActiveFormId(formId);

                    // 操作方式
                    replaceOperations(operationsPojos, ruleType, sourceActiveFormId, formId);
                } else {
                    PaasFormEntity paasFormEntity = sourceToNewFormMap.get(ruleInfoEntity.getActiveFormId());
                    if (Objects.nonNull(paasFormEntity)) {
                        dealWithActiveBusinessRule(ruleInfoEntity, paasFormEntity);
                    }
                }
            }

            // 失效规则
            if (StringUtil.isNotEmpty(ruleInfoEntity.getInactiveName())) {
                // 高级规则(公式) or 简易规则
                Integer ruleType = ruleInfoEntity.getInactiveType();
                // 操作方式
                List<OperationsPojo> operationsPojos = JSONArray.parseArray(ruleInfoEntity.getInactiveOperations(), OperationsPojo.class);
                if (sourceFormMirrorMap.containsKey(ruleInfoEntity.getInactiveFormId())) {
                    // 同一个镜像里的表单，可直接替换 （对应关系 sourceFormId -> mirrorFormId -> newFormId）

                    // 目标表单
                    ruleInfoEntity.setInactiveAppId(appId);
                    if (isWorkOrder) {
                        ruleInfoEntity.setInactiveMenuId(paasMenuMap.get(0L));
                    } else {
                        Long mirrorMenuId = sourceMenuMirrorMap.get(ruleInfoEntity.getInactiveMenuId());
                        ruleInfoEntity.setInactiveMenuId(paasMenuMap.get(mirrorMenuId));
                    }

                    Long sourceInactiveFormId = ruleInfoEntity.getInactiveFormId();
                    Long mirrorFormId = sourceFormMirrorMap.get(sourceInactiveFormId);
                    Long formId = paasFormMap.get(mirrorFormId);
                    ruleInfoEntity.setInactiveFormId(formId);

                    // 操作方式
                    replaceOperations(operationsPojos, ruleType, sourceInactiveFormId, formId);
                } else {
                    PaasFormEntity paasFormEntity = sourceToNewFormMap.get(ruleInfoEntity.getInactiveFormId());
                    if (Objects.nonNull(paasFormEntity)) {
                        dealWithInactiveBusinessRule(ruleInfoEntity, paasFormEntity);
                    }
                }
            }
        }

        return JSONObject.toJSONString(businessRuleList);
    }

    /**
     * 生效业务规则操作
     * @param ruleInfoEntity
     * @param paasFormEntity
     * @author xingxing.xiao
     * @date 2020/06/12 16:52
     */
    private void dealWithActiveBusinessRule(BusinessRuleInfoEntity ruleInfoEntity, PaasFormEntity paasFormEntity) throws XbbException {
        Long sourceActiveFormId = ruleInfoEntity.getActiveFormId();
        ruleInfoEntity.setActiveAppId(paasFormEntity.getAppId());
        ruleInfoEntity.setActiveMenuId(paasFormEntity.getMenuId());
        ruleInfoEntity.setActiveFormId(paasFormEntity.getId());

        // 操作方式
        Integer ruleType = ruleInfoEntity.getActiveType();
        List<OperationsPojo> operationsPojos = JSONArray.parseArray(ruleInfoEntity.getActiveOperations(), OperationsPojo.class);
        replaceOperations(operationsPojos, ruleType, sourceActiveFormId, paasFormEntity.getId());
    }

    /**
     * 失效业务规则操作
     * @param ruleInfoEntity
     * @param paasFormEntity
     * @author xingxing.xiao
     * @date 2020/06/12 16:52
     */
    private void dealWithInactiveBusinessRule(BusinessRuleInfoEntity ruleInfoEntity, PaasFormEntity paasFormEntity) throws XbbException {

        Long sourceInactiveFormId = ruleInfoEntity.getInactiveFormId();
        ruleInfoEntity.setInactiveAppId(paasFormEntity.getAppId());
        ruleInfoEntity.setInactiveMenuId(paasFormEntity.getMenuId());
        ruleInfoEntity.setInactiveFormId(paasFormEntity.getId());

        // 操作方式只需处理高级规则里的目标表单
        Integer ruleType = ruleInfoEntity.getInactiveType();
        List<OperationsPojo> operationsPojos = JSONArray.parseArray(ruleInfoEntity.getInactiveOperations(), OperationsPojo.class);
        replaceOperations(operationsPojos, ruleType, sourceInactiveFormId, paasFormEntity.getId());

    }

    /**
     * 操作方式-只需处理高级规则里的目标表单
     * @param operationsPojos
     * @param ruleType
     * @param sourceFormId
     * @param formId
     * @author xingxing.xiao
     * @date 2020/06/12 20:47
     */
    private void replaceOperations (List<OperationsPojo> operationsPojos, Integer ruleType, Long sourceFormId, Long formId) throws XbbException {

        for (OperationsPojo operation : operationsPojos) {
            if (Objects.equals(ruleType, RuleVersionEnum.ADVANCED.getType())) {
                FormulaPoJo formulaInfo = operation.getFormulaInfo();
                String formula = formulaInfo.getFormula();

                // 暂定在此处判断下是否关联了部门和人员
                String[] formulaArray = RegexUtil.getFormulaAttr(formula);
                for (String formulaAttr : formulaArray) {
                    if (formulaAttr.contains("user.")) {
                        throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403043, MirrorErrorCodeEnum.API_ERROR_403043.getMsg(), I18nMessageUtil.getMessage(CommonConstant.EMPLOYEE));
                    } else if (formulaAttr.contains("dept.")) {
                        throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403043, MirrorErrorCodeEnum.API_ERROR_403043.getMsg(), I18nMessageUtil.getMessage(CommonConstant.DEPARTMENT));
                    }
                }

                // 替换表单
                String newFormula = formula.replaceAll("\\{" + sourceFormId, "\\{" + formId);
                formulaInfo.setFormula(newFormula);
                String labelMapStr = formulaInfo.getLabelMap().toJSONString();
                String labelMap = labelMapStr.replaceAll("\"" + sourceFormId, "\"" + formId);
                formulaInfo.setLabelMap(JSONObject.parseObject(labelMap));
            }
        }
    }

    /**
     * 业务规则-校验版本以及白名单
     * @param businessRuleWhiteDTO 参数
     * @return true：不可以使用业务规则；false：可以使用
     * @author xingxing.xiao
     */
    private BusinessRuleWhiteVO validateVersionAndWhite(BusinessRuleWhiteDTO businessRuleWhiteDTO) throws XbbException {
        BusinessRuleWhiteVO businessRuleWhiteVO;
        // 增加 i18n 参数
        Locale locale = LocaleContextHolder.getLocale();
        String lang = locale.toString();
        XbbResponse<BusinessRuleWhiteVO> response = formBusinessRuleFeignClient.validateVersionAndWhite(businessRuleWhiteDTO, lang);
        if (response.isSuccess()) {
            businessRuleWhiteVO = response.getResult();
        } else {
            throw new XbbException(response.getCode(), response.getMsg());
        }
        return businessRuleWhiteVO;
    }

    /**
     * 校验自定义表单
     * @param corpid
     * @param mirrorFormExplainList
     * @param sourceFormMirrorMap   原表单与镜像对应关系
     * @param sourceCorpid 用来创建镜像的公司id
     * @param sourceToNewFormIdMap
     * @param businessTypeToNewFormMap
     * @param workOrderDefaultToNewFormMap
     * @author xingxing.xiao
     * @date 2020/04/14 08:44
     */
    private void checkPaasFormField (String corpid, List<MirrorFormExplainEntity> mirrorFormExplainList, Map<Long, Long> sourceFormMirrorMap, String sourceCorpid, HashMap<Long, PaasFormEntity> sourceToNewFormIdMap, HashMap<Integer, PaasFormEntity> businessTypeToNewFormMap, HashMap<Integer, WorkOrderFormEntity> workOrderDefaultToNewFormMap) throws XbbException {
        // 新旧对应表单
        HashMap<Long, PaasFormEntity> formEntityMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        // 关联系统表单businessType
        Set<Integer> sysFormBusinessTypes = new HashSet<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        // 关联工单表单businessType
        Set<Integer> workOrderBusinessTypes = new HashSet<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);

        // 找出关联表单
        HashMap<Long, HashSet<String>> formIdToAttrMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (MirrorFormExplainEntity entity : mirrorFormExplainList) {
            // 处理字段解释
            List<FieldAttrEntity> explainList =  JSONArray.parseArray(entity.getExplains(), FieldAttrEntity.class);
            for (FieldAttrEntity item : explainList) {
                getFormIdToAttrMap(item, sourceFormMirrorMap, formIdToAttrMap, sysFormBusinessTypes, workOrderBusinessTypes);
                // 子表单
                if (Objects.nonNull(item.getSubForm())) {
                    List<? extends FieldAttrEntity> subExplainList = item.getSubForm().getItems();
                    // 加上判空
                    if (Objects.nonNull(subExplainList)) {
                        for (FieldAttrEntity fieldAttrEntity : subExplainList) {
                            getFormIdToAttrMap(fieldAttrEntity, sourceFormMirrorMap, formIdToAttrMap, sysFormBusinessTypes, workOrderBusinessTypes);
                        }
                    }
                }
            }
        }

        if (workOrderBusinessTypes.size() > 0) {
            WorkOrderFormEntity workOrderFormEntity = getWorkOrderDefaultForm(corpid);
            workOrderDefaultToNewFormMap.put(XbbRefTypeEnum.WORK_ORDER.getCode(), workOrderFormEntity);
        }

        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        if (sysFormBusinessTypes.size() > 0) {
            param.put(StringConstant.CORPID, corpid);
            param.put("businessTypeList", sysFormBusinessTypes);
            param.put("enable", 1);
            param.put(StringConstant.DEL, DelEnum.NORMAL.getDel());
            param.put("orderByStr", "id DESC");
            List<PaasFormEntity> formEntities = paasFormModel.findEntity(param);
            formEntities.forEach(item -> businessTypeToNewFormMap.put(item.getBusinessType(), item));
            for (Integer businessType : sysFormBusinessTypes) {
                if (!businessTypeToNewFormMap.containsKey(businessType)) {
                    // 查询关联的应用名称，作提示用
                    JXCMenuEnum byCode = JXCMenuEnum.getByCode(businessType);
                    if (Objects.nonNull(byCode)) {
                        throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403009, MirrorErrorCodeEnum.API_ERROR_403009.getMsg(), SystemAppMenuEnum.JXC.getName());
                    }
                }
            }
        }

        // 暂时就校验表单吧
        if (formIdToAttrMap.size() > 0) {

            List<Long> sourceFormIdIn = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            HashSet<Long> installSourceFormIdIn = new HashSet<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            List<Long> formIdIn = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            List<Long> newFormIdIn = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);

            sourceFormIdIn.addAll(formIdToAttrMap.keySet());

            param.clear();
            param.put(StringConstant.CORPID, corpid);
            param.put("sourceFormIdIn", sourceFormIdIn);
            param.put("businessType", 0);
            param.put(StringConstant.DEL, DelEnum.NORMAL.getDel());
            param.put("orderByStr", "id asc");
            List<FormInstallEntity> installFormList = formInstallModel.findEntitys(param);
            if (installFormList.isEmpty()) {
                // 任取一个表单做提示
                throwErrorMessage(sourceFormIdIn.get(0), sourceCorpid);
            } else {
                installFormList.forEach(item -> {
                    formIdIn.add(item.getFormId());
                    installSourceFormIdIn.add(item.getSourceFormId());
                });

                // 要保证每个都有
                for (Long sourceFormId : sourceFormIdIn) {
                    if (!installSourceFormIdIn.contains(sourceFormId)) {
                        // 提示缺少的表单
                        throwErrorMessage(sourceFormId, sourceCorpid);
                    }
                }

                param.clear();
                param.put(StringConstant.CORPID, corpid);
                param.put("idIn", formIdIn);
                param.put(StringConstant.DEL, DelEnum.NORMAL.getDel());
                List<PaasFormEntity> paasformList = paasFormModel.findEntity(param);
                paasformList.forEach(item -> {
                    newFormIdIn.add(item.getId());
                    formEntityMap.put(item.getId(), item);
                });

                // 取最新的formId 与 sourceFormId 匹配
                installFormList.forEach(item -> {
                    if (newFormIdIn.contains(item.getFormId())) {
                        PaasFormEntity formEntity = formEntityMap.get(item.getFormId());
                        sourceToNewFormIdMap.put(item.getSourceFormId(), formEntity);
                    }
                });

                // 判断是否有表单
                for (FormInstallEntity item : installFormList) {
                    Long sourceFormId = item.getSourceFormId();
                    if (!sourceToNewFormIdMap.containsKey(sourceFormId)) {
                        // 提示缺少的表单
                        throwErrorMessage(sourceFormId, sourceCorpid);
                    }
                }
            }
        }
    }

    /**
     * 找出关联的自定义表单及表单里的字段（重点：此处只处理自定义表单）
     * @param item 字段实例
     * @param sourceFormMirrorMap  原表单与镜像对应关系
     * @param formIdToAttrMap  所需自定义表单及表单里的字段
     * @param workOrderBusinessTypes 关联系统表单businessType
     * @param sysFormBusinessTypes 关联工单表单businessType
     * @author xingxing.xiao
     */
    private void getFormIdToAttrMap (FieldAttrEntity item, Map<Long, Long> sourceFormMirrorMap, HashMap<Long, HashSet<String>> formIdToAttrMap, Set<Integer> sysFormBusinessTypes, Set<Integer> workOrderBusinessTypes) {
        // 数据联动 或者 下拉框-数据联动
        boolean dataRelyFlag = Objects.nonNull(item.getLinkForm()) && Objects.equals(item.getComboType(), ComboTypeEnum.DATARELY.getType());
        if (Objects.equals(item.getDefaultAttr().getDefaultType(), ComboTypeEnum.DATARELY.getAlias()) || dataRelyFlag) {
            RelyPoJo relyPoJo = item.getDefaultAttr().getRely();
            if (Objects.nonNull(relyPoJo)) {
                // 不是同一个镜像里的表单要做处理（因为同一个镜像里的表单可直接替换）
                if (!sourceFormMirrorMap.containsKey(relyPoJo.getSourceFormId())) {
                    Integer businessType = relyPoJo.getSourceBusinessType();
                    if (Objects.equals(relyPoJo.getSourceSaasMark(), SaasMarkEnum.SAAS.getCode())) {
                        // 区分工单和其他应用
                        if (Objects.equals(businessType, XbbRefTypeEnum.WORK_ORDER.getCode())) {
                            workOrderBusinessTypes.add(businessType);
                        } else {
                            // 客户、合同和单模板处理
                            sysFormBusinessTypes.add(businessType);
                        }
                    } else if (Objects.equals(relyPoJo.getSourceSaasMark(), SaasMarkEnum.PAAS.getCode())) {
                        Long sourceFormId = relyPoJo.getSourceFormId();
                        if (formIdToAttrMap.containsKey(sourceFormId)) {
                            HashSet<String> attrList = formIdToAttrMap.get(sourceFormId);
                            attrList.add(relyPoJo.getLinkField());
                            attrList.add(relyPoJo.getTargetField());
                            formIdToAttrMap.put(sourceFormId, attrList);
                        } else {
                            HashSet<String> attrList = new HashSet<>();
                            attrList.add(relyPoJo.getLinkField());
                            attrList.add(relyPoJo.getTargetField());
                            formIdToAttrMap.put(sourceFormId, attrList);
                        }
                    }
                }
            }
        }

        // 关联数据
        if (Objects.nonNull(item.getLinkInfo())) {
            LinkInfoPojo linkInfo = item.getLinkInfo();
            Long linkFormId = linkInfo.getLinkFormId();
            if (!sourceFormMirrorMap.containsKey(linkFormId)) {
                Integer businessType = linkInfo.getLinkBusinessType();
                if (Objects.equals(linkInfo.getLinkSaasMark(), SaasMarkEnum.SAAS.getCode())) {
                    // 区分工单和其他应用
                    if (Objects.equals(businessType, XbbRefTypeEnum.WORK_ORDER.getCode())) {
                        workOrderBusinessTypes.add(businessType);
                    } else {
                        // 客户、合同和单模板处理
                        sysFormBusinessTypes.add(businessType);
                    }
                } else if (Objects.equals(linkInfo.getLinkSaasMark(), SaasMarkEnum.PAAS.getCode())) {
                    LinkKeyFieldInfoPojo linkKeyAttr = linkInfo.getLinkKeyAttr();
                    List<LinkedAttrPoJo> linkedAttr = linkInfo.getLinkedAttr();
                    if (formIdToAttrMap.containsKey(linkFormId)) {
                        HashSet<String> attrList = formIdToAttrMap.get(linkFormId);
                        attrList.add(linkKeyAttr.getAttr());
                        for (LinkedAttrPoJo entity : linkedAttr) {
                            attrList.add(entity.getAttr());
                        }
                        formIdToAttrMap.put(linkFormId, attrList);
                    } else {
                        HashSet<String> attrList = new HashSet<>();
                        attrList.add(linkKeyAttr.getAttr());
                        for (LinkedAttrPoJo entity : linkedAttr) {
                            attrList.add(entity.getAttr());
                        }
                        formIdToAttrMap.put(linkFormId, attrList);
                    }
                }
            }
        }

        // 下拉框 - 关联表单
        if (Objects.nonNull(item.getLinkForm())) {
            if (Objects.equals(item.getComboType(), ComboTypeEnum.LINKFORM.getType())) {
                LinkFormPoJo linkForm = item.getLinkForm();
                if (!sourceFormMirrorMap.containsKey(linkForm.getSourceFormId())) {
                    Integer businessType = linkForm.getSourceBusinessType();
                    if (Objects.equals(linkForm.getSourceSaasMark(), SaasMarkEnum.SAAS.getCode())) {
                        // 区分工单和其他应用
                        if (Objects.equals(businessType, XbbRefTypeEnum.WORK_ORDER.getCode())) {
                            workOrderBusinessTypes.add(businessType);
                        } else {
                            // 客户、合同和单模板处理
                            sysFormBusinessTypes.add(businessType);
                        }
                    } else if (Objects.equals(linkForm.getSourceSaasMark(), SaasMarkEnum.PAAS.getCode())) {
                        Long sourceFormId = linkForm.getSourceFormId();
                        if (formIdToAttrMap.containsKey(sourceFormId)) {
                            HashSet<String> attrList = formIdToAttrMap.get(sourceFormId);
                            attrList.add(linkForm.getDisplayField());
                            formIdToAttrMap.put(sourceFormId, attrList);
                        } else {
                            HashSet<String> attrList = new HashSet<>();
                            attrList.add(linkForm.getDisplayField());
                            formIdToAttrMap.put(sourceFormId, attrList);
                        }
                    }
                }
            }
        }
    }

    /**
     * 安装自定应用镜像
     * @throws XbbException
     * @author xingxing.xiao
     */
    public Long installCustomeMirror(String corpid, Long mirrorId, String sourceCorpid, String creatorId, MirrorAppEntity mirrorAppEntity, String mirrorName, MirrorInstallDTO mirrorInstallDTO) throws XbbException {
        Long appInstallId;
        try {
            long now = DateUtil.getInt();
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);

            // 镜像与原表单对应关系(安装时用)
            Map<Long, Long> mirrorFormMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            // 原表单与镜像对应关系(处理解释时用)
            Map<Long, Long> sourceFormMirrorMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("mirrorId", mirrorId);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorFormEntity> mirrorFormList = mirrorFormModel.findEntitys(param);
            for (MirrorFormEntity entity : mirrorFormList) {
                mirrorFormMap.put(entity.getId(), entity.getSourceFormId());
                sourceFormMirrorMap.put(entity.getSourceFormId(), entity.getId());
            }

            param.clear();
            param.put("mirrorId", mirrorId);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorBusinessRuleInfoEntity> mirrorRuleList = mirrorBusinessRuleInfoModel.findEntitys(param);

            param.clear();
            param.put("mirrorId", mirrorId);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorFormExplainEntity> mirrorFormExplainList = mirrorFormExplainModel.findEntitys(param);

            param.clear();
            param.put("mirrorId", mirrorId);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorStageProcessEntity> mirrorStageProcessEntityList = mirrorStageProcessModel.findEntitys(param);

            param.clear();
            param.put("mirrorId", mirrorId);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorStageEntity> mirrorStageEntityList = mirrorStageModel.findEntitys(param);

            param.clear();
            param.put("mirrorId", mirrorId);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorStageWorkEntity> mirrorStageWorkEntityList = mirrorStageWorkModel.findEntitys(param);

            param.clear();
            param.put("mirrorId", mirrorId);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorLowCodeEntity> mirrorLowCodeEntityList = mirrorLowCodeModel.findEntities(param);

            // 提取业务规则并判断表单及字段
            HashMap<Long, PaasFormEntity> sourceToNewFormMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            if (CollectionsUtil.isNotEmpty(mirrorRuleList)) {
                BusinessRuleWhiteDTO businessRuleWhiteDTO = new BusinessRuleWhiteDTO();
                BeanUtil.copyProperties(mirrorInstallDTO, businessRuleWhiteDTO);
                BusinessRuleWhiteVO businessRuleWhiteVO = validateVersionAndWhite(businessRuleWhiteDTO);
                Integer feeType = businessRuleWhiteVO.getFeeType();
                // 标准版和高级版不能安装业务规则
                if (!Objects.equals(feeType, 3)) {
                    throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403047);
                }
                sourceToNewFormMap = checkBusinessRuleForm(corpid, mirrorRuleList, sourceFormMirrorMap, sourceCorpid);
            }

            // 校验自定义表单及字段解释是否存在，还有默认系统表单和默认工单表单信息(找businessType对应关系，后面处理解释中关联字段时会用上)
            HashMap<Long, PaasFormEntity> sourceToNewFormIdMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            HashMap<Integer, PaasFormEntity> businessTypeToNewFormMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            HashMap<Integer, WorkOrderFormEntity> workOrderDefaultToNewFormMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            checkPaasFormField(corpid, mirrorFormExplainList, sourceFormMirrorMap, sourceCorpid, sourceToNewFormIdMap, businessTypeToNewFormMap, workOrderDefaultToNewFormMap);

            // 1. 复制应用
            PaasAppEntity paasAppEntity = new PaasAppEntity();
            BeanUtil.copyProperties(mirrorAppEntity, paasAppEntity);
            paasAppEntity.setName(mirrorName);
            paasAppEntity.setCorpid(corpid);
            Integer appMaxSort = getAppMaxSort(corpid);
            paasAppEntity.setSort(appMaxSort + 1);
            paasAppEntity.setCreatorId(creatorId);
            paasAppEntity.setAddTime(now);
            paasAppEntity.setUpdateTime(now);
            paasAppModel.insert(paasAppEntity);
            Long appId = paasAppEntity.getId();
            //应用是市场管理的话，刷新缓存
            String marketManagementAlias = SystemAppMenuEnum.MARKET_MANAGEMENT.getAlias();
            String alias = paasAppEntity.getAlias();
            Integer enable = paasAppEntity.getEnable();
            if(Objects.equals(alias,marketManagementAlias)){
                if(Objects.nonNull(enable)){
                    String key = corpid + "_" + alias;
                    //缓存 应用的启用状态，时间为一周
                    paasRedisHelper.setValue(RedisPrefixConstant.APP_ENABLE_STATUS, key,String.valueOf(enable),RedisConstant.HUGE_DURATION);
                }
            }
            // 2. 复制菜单
            List<PaasMenuEntity> parentMenuList = new ArrayList<>();
            List<PaasMenuEntity> childMenuList = new ArrayList<>();
            param.clear();
            param.put("mirrorId", mirrorId);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorMenuEntity> mirrorMenuList = mirrorMenuModel.findEntitys(param);
            // 镜像与原菜单对应关系(安装时用)
            Map<Long, Long> mirrorMenuMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            // 原菜单与镜像对应关系(处理解释时用)
            Map<Long, Long> sourceMenuMirrorMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            for (MirrorMenuEntity entity : mirrorMenuList) {
                if (Objects.equals(entity.getParentId(), 0L)) {
                    setMenuList(entity, parentMenuList, corpid, appId, creatorId, now);
                } else {
                    setMenuList(entity, childMenuList, corpid, appId, creatorId, now);
                }
                mirrorMenuMap.put(entity.getId(), entity.getSourceMenuId());
                sourceMenuMirrorMap.put(entity.getSourceMenuId(), entity.getId());
            }
            paasMenuModel.insertBatch(parentMenuList);
            Map<Long, Long> parentMenuMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            // 镜像与菜单对应关系
            Map<Long, Long> paasMenuMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            for (PaasMenuEntity parentMenu : parentMenuList) {
                parentMenuMap.put(parentMenu.getMirrorMenuId(), parentMenu.getId());
                paasMenuMap.put(parentMenu.getMirrorMenuId(), parentMenu.getId());
            }
            for (PaasMenuEntity childMenu : childMenuList) {
                if (Objects.nonNull(parentMenuMap.get(childMenu.getParentId()))) {
                    childMenu.setParentId(parentMenuMap.get(childMenu.getParentId()));
                }
            }
            if (!childMenuList.isEmpty()) {
                paasMenuModel.insertBatch(childMenuList);
                for (PaasMenuEntity childMenu : childMenuList) {
                    paasMenuMap.put(childMenu.getMirrorMenuId(), childMenu.getId());
                }
            }
            // 3. 复制表单
            List<PaasFormEntity> formList = new ArrayList<>();
            for (MirrorFormEntity entity : mirrorFormList) {
                // 不复制流程，所以不开启流程表单(isProcessForm=0)
                Long menuId = paasMenuMap.get(entity.getMirrorMenuId());
                // 处理关联列表(不复制，为 [], 在 form 表的association_list)
                String associationList = new JSONArray().toJSONString();
                String permission = new JSONArray().toJSONString();
                // 默认没有规则，后面若可以复制规则，再更新 businessRule = 1
                String businessRules = "0";
                PaasFormEntity formEntity = new PaasFormEntity(corpid, appId, menuId, entity.getName(), entity.getSaasMark(), entity.getBusinessType(), entity.getEnable(), 0, entity.getVerifyRule(), entity.getDisableFieldAssign(), entity.getFrontCache(), entity.getTitleType(), entity.getCustomTitle(), associationList, businessRules, entity.getSurcharge(), entity.getOpenDistribution(), entity.getPriceAssociation(), entity.getSummary(), entity.getLabels(), entity.getCoOpPerms(), permission, creatorId, now, now, entity.getDel(), entity.getFieldPosition(), entity.getHidden(), entity.getDistributorMark());
                formEntity.setMirrorFormId(entity.getId());
                formEntity.setMirrorAppId(entity.getMirrorAppId());
                formEntity.setMirrorMenuId(entity.getMirrorMenuId());
                formEntity.setSort(0);
                formList.add(formEntity);
            }
            if (!formList.isEmpty()) {
                paasFormModel.insertBatch(formList);
                // 镜像表单与新生成表单对应关系
                Map<Long, Long> paasFormMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                for (PaasFormEntity entity : formList) {
                    paasFormMap.put(entity.getMirrorFormId(), entity.getId());
                }

                // 3.1 复制业务规则
                if (CollectionsUtil.isNotEmpty(mirrorRuleList)) {
                    List<BusinessRuleInfoEntity> businessRuleList = new ArrayList<>();
                    for (MirrorBusinessRuleInfoEntity entity : mirrorRuleList) {
                        Long menuId = paasMenuMap.get(entity.getMirrorMenuId());
                        Long formId = paasFormMap.get(entity.getMirrorFormId());
                        BusinessRuleInfoEntity ruleInfoEntity = new BusinessRuleInfoEntity(corpid, entity.getSaasMark(), entity.getBusinessType(), formId, appId, menuId, entity.getActiveName(), entity.getActiveType(), entity.getActiveSaasMark(), entity.getActiveBusinessType(),
                                entity.getActiveFormId(), entity.getActiveMenuId(), entity.getActiveAppId(), entity.getActiveOperationType(), entity.getActivePreConditions(), entity.getActiveFilterConditions(), entity.getActiveOperations(), entity.getActiveMemo(), entity.getActiveEnable(),
                                entity.getInactiveName(), entity.getInactiveType(), entity.getInactiveSaasMark(), entity.getInactiveBusinessType(), entity.getInactiveFormId(), entity.getInactiveMenuId(), entity.getInactiveAppId(), entity.getInactiveOperationType(), entity.getInactivePreConditions(), entity.getInactiveFilterConditions(), entity.getInactiveOperations(), entity.getInactiveMemo(), entity.getInactiveEnable(), entity.getPriority(), creatorId, now, now, entity.getDel());
                        businessRuleList.add(ruleInfoEntity);
                    }
                    if (!businessRuleList.isEmpty()) {
                        // 处理业务规则-表单等替换成新的
                        dealWithBusinessRules(businessRuleList, appId, paasMenuMap, paasFormMap, corpid, sourceMenuMirrorMap, sourceFormMirrorMap, sourceToNewFormMap, false);
                        businessRuleInfoModel.insertBatch(businessRuleList);

                        // 批量更新表单表的businessRule=1
                        Set<Long> formIdSets = new HashSet<>();
                        businessRuleList.forEach(item -> formIdSets.add(item.getFormId()));
                        List<Long> ruleFormIds = new ArrayList<>(formIdSets);
                        paasFormModel.updateBusinessRuleBatch(ruleFormIds, corpid, "1");
                    }
                }

                // 4. 复制表单解释
                List<PaasFormExplainEntity> formExplainList = new ArrayList<>();
                for (MirrorFormExplainEntity entity : mirrorFormExplainList) {
                    Long menuId = paasMenuMap.get(entity.getMirrorMenuId());
                    Long formId = paasFormMap.get(entity.getMirrorFormId());
                    PaasFormExplainEntity formExplainEntity = new PaasFormExplainEntity(corpid, appId, menuId, formId, entity.getEnable(), entity.getExplains(), creatorId, now, now, entity.getDel(), entity.getSaasMark(), entity.getBusinessType(), DistributorMarkEnum.OTHER.getCode());
                    // 处理字段解释
                    List<FieldAttrEntity> explainList =  JSONArray.parseArray(formExplainEntity.getExplains(), FieldAttrEntity.class);
                    String explains = dealWithExplains(explainList, appId, paasMenuMap, paasFormMap, corpid, sourceMenuMirrorMap, sourceFormMirrorMap, sourceToNewFormIdMap, businessTypeToNewFormMap, workOrderDefaultToNewFormMap, false);
                    formExplainEntity.setExplains(explains);
                    formExplainList.add(formExplainEntity);
                }
                if (!formExplainList.isEmpty()) {
                    paasFormExplainModel.insertBatch(formExplainList);
                }


                StageInstallPojo stageInstallPojo = new StageInstallPojo(corpid,paasFormMap,mirrorStageProcessEntityList,mirrorStageEntityList,mirrorStageWorkEntityList);
                // 5. 复制阶段推进器的 3 张表
                installStageThruster(stageInstallPojo);

                // 6. 复制打印模板
                List<PaasPrintTemplateEntity> printTemplateList = new ArrayList<>();
                param.clear();
                param.put("mirrorId", mirrorId);
                param.put("del", DelEnum.NORMAL.getDel());
                List<MirrorPrintTemplateEntity> mirrorPrintTemplateList = mirrorPrintTemplateModel.findEntitys(param);
                for (MirrorPrintTemplateEntity entity : mirrorPrintTemplateList) {
                    Long menuId = paasMenuMap.get(entity.getMirrorMenuId());
                    Long formId = paasFormMap.get(entity.getMirrorFormId());
                    PaasPrintTemplateEntity printTemplateEntity = new PaasPrintTemplateEntity(corpid, appId, menuId, formId, entity.getName(), entity.getEnable(), entity.getHtml(), entity.getConfigInfo(), entity.getBusinessType(), creatorId, now, now, entity.getDel());
                    printTemplateList.add(printTemplateEntity);
                }
                if (!printTemplateList.isEmpty()) {
                    paasPrintTemplateDao.insertBatch(printTemplateList);
                }
                // 7. 复制低代码
                installLowCode(corpid, creatorId, appId, paasFormMap, mirrorLowCodeEntityList);

            }

            // 记录镜像应用安装信息
            appInstallId = recordAppInstall(formList, paasMenuMap, mirrorMenuMap, mirrorFormMap, corpid, appId, mirrorAppEntity.getSourceAppId(), sourceCorpid, creatorId, mirrorId, MirrorApplicationTypeEnum.CUSTOM.getCode(), 0L);
        } catch (XbbException e) {
            throw e;
        } catch (Exception e) {
            LOG.error("installCustomeMirror error: ", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        return appInstallId;
    }


    /**
     * 安装阶段推进器的 3 张表:StageProcess、Stage、StageWork
     * @param stageInstallPojo
     */
    private void installStageThruster(StageInstallPojo stageInstallPojo) {

        installStageProcess(stageInstallPojo);

        installStage(stageInstallPojo);

        installStageWork(stageInstallPojo);

    }

    private void installStageProcess(StageInstallPojo stageInstallPojo) {
        String corpid = stageInstallPojo.getCorpid();
        Map<Long, Long> mirrorFormId2NowMap = stageInstallPojo.getMirrorFormId2NowMap();
        Map<Long, Long> mirrorStageProcessId2NowMap = stageInstallPojo.getMirrorStageProcessId2NowMap();
        List<MirrorStageProcessEntity> mirrorStageProcessEntityList = stageInstallPojo.getMirrorStageProcessEntityList();

        // stageProcess
        if (!mirrorStageProcessEntityList.isEmpty()) {
            List<StageProcessEntity> insertList = new ArrayList<>();
            for (MirrorStageProcessEntity item : mirrorStageProcessEntityList) {
                //清空人员
                item.setAllowNewSelectUser(null);

                StageProcessEntity entity = new StageProcessEntity(corpid, mirrorFormId2NowMap.get(item.getMirrorFormId()), item.getName(), item.getVersionName(),
                        item.getVersionId(), item.getEnable(), item.getStageUnit(), item.getAllowBack(), item.getAllowNewSelect(), item.getAllowNewSelectUser(),
                        item.getShowTimeUse(), item.getShowTimeStay(), item.getShowWorkCompletion(), item.getShowAverageCompletionTime(), item.getTriggerCondition(),
                        item.getIsArchive(), item.getCreatorId(), item.getOldTransferVersion(), item.getDel());
                entity.setMirrorStageProcessId(item.getId());
                insertList.add(entity);
            }
            if (!insertList.isEmpty()) {
                stageProcessModel.insertBatch(insertList);

                insertList.forEach(item -> {
                    mirrorStageProcessId2NowMap.put(item.getMirrorStageProcessId(), item.getId());
                });
            }
        }
    }

    private void installStage(StageInstallPojo stageInstallPojo) {
        List<MirrorStageEntity> mirrorStageEntityList = stageInstallPojo.getMirrorStageEntityList();
        if (mirrorStageEntityList.isEmpty()) {
            return;
        }
        List<MirrorStageEntity> mirrorCustomerStageList = new ArrayList<>();
        List<MirrorStageEntity> mirrorOpportunityStageList = new ArrayList<>();
        List<MirrorStageEntity> mirrorClueStageList = new ArrayList<>();
        List<MirrorStageEntity> mirrorPaasStageList = new ArrayList<>();

        mirrorStageEntityList.forEach(item -> {
            if (Objects.equals(item.getBusinessType(), XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode())) {
                mirrorCustomerStageList.add(item);
            } else if (Objects.equals(item.getBusinessType(), XbbRefTypeEnum.SALES_OPPORTUNITY.getCode())) {
                mirrorOpportunityStageList.add(item);
            } else if (Objects.equals(item.getBusinessType(), XbbRefTypeEnum.CLUE.getCode())) {
                mirrorClueStageList.add(item);
            } else {
                mirrorPaasStageList.add(item);
            }
        });

        installSingleStage(mirrorPaasStageList, SaasMarkEnum.PAAS.getCode(), 0, stageInstallPojo);
        installSingleStage(mirrorCustomerStageList, SaasMarkEnum.SAAS.getCode(), XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode(), stageInstallPojo);
        installSingleStage(mirrorOpportunityStageList, SaasMarkEnum.SAAS.getCode(), XbbRefTypeEnum.SALES_OPPORTUNITY.getCode(), stageInstallPojo);
        installSingleStage(mirrorClueStageList, SaasMarkEnum.SAAS.getCode(), XbbRefTypeEnum.CLUE.getCode(), stageInstallPojo);

    }

    private void installSingleStage(List<MirrorStageEntity> mirrorStageEntityList, Integer saasMark, Integer businessType, StageInstallPojo stageInstallPojo) {
        if (mirrorStageEntityList.isEmpty()) {
            return;
        }
        String corpid = stageInstallPojo.getCorpid();
        Map<Long, Long> mirrorFormId2NowMap = stageInstallPojo.getMirrorFormId2NowMap();
        Map<Long, Long> mirrorStageProcessId2NowMap = stageInstallPojo.getMirrorStageProcessId2NowMap();
        Map<Long, Long> mirrorStageId2NowMap = stageInstallPojo.getMirrorStageId2NowMap();

        List<PaasStageEntity> insertList = new ArrayList<>();
        StageHandle stageHandle = stageHandleFactory.getStageHandle(saasMark, businessType);
        for (MirrorStageEntity item : mirrorStageEntityList) {
            //清空人员
            item.setExecutor(null);

            PaasStageEntity entity = new PaasStageEntity(corpid, mirrorFormId2NowMap.get(item.getMirrorFormId()), mirrorStageProcessId2NowMap.get(item.getMirrorStageProcessId()),
                    item.getName(), item.getCode(), item.getIntoProportion(), item.getOutProportion(), item.getType(), item.getEnable(), item.getSort(), item.getStayLimit(),
                    item.getStayLimitTime(), item.getStayLimitTimeUnit(), item.getEnableStageWarningColor(), item.getStageWarningColor(), item.getExecutor(), item.getPreConditions(),
                    item.getAllowSkipStage(), item.getOpenStatus(), item.getBusinessRelatedStatus(), item.getExtraSetting(), item.getAllowReactive(), item.getAllowReactiveStage(),
                    item.getEnableAutoJump(), item.getEnableJumpEnd(), item.getIsDefault(), item.getLinkOption(), item.getAllowCreateContractSkipStage());
            entity.setMirrorStageId(item.getId());
            insertList.add(entity);
        }
        if (!insertList.isEmpty()) {
            stageHandle.insertBatch(insertList);

            insertList.forEach(item -> {
                mirrorStageId2NowMap.put(item.getMirrorStageId(), item.getId());
            });
        }
    }

    private void installStageWork(StageInstallPojo stageInstallPojo) {
        String corpid = stageInstallPojo.getCorpid();
        Map<Long, Long> mirrorFormId2NowMap = stageInstallPojo.getMirrorFormId2NowMap();
        Map<Long, Long> mirrorStageProcessId2NowMap = stageInstallPojo.getMirrorStageProcessId2NowMap();
        Map<Long, Long> mirrorStageId2NowMap = stageInstallPojo.getMirrorStageId2NowMap();
        List<MirrorStageWorkEntity> mirrorStageWorkEntityList = stageInstallPojo.getMirrorStageWorkEntityList();

        if (mirrorStageWorkEntityList.isEmpty()) {
            return;
        }

        List<StageWorkEntity> insertList = new ArrayList<>();
        for (MirrorStageWorkEntity item : mirrorStageWorkEntityList) {
            item.setExecutor(null);
            StageWorkEntity entity = new StageWorkEntity(corpid, mirrorFormId2NowMap.get(item.getMirrorFormId()), item.getBusinessType(), item.getName(),
                    item.getRequired(), item.getSort(), item.getStageCode(), mirrorStageProcessId2NowMap.get(item.getMirrorStageProcessId()),
                    mirrorStageId2NowMap.get(item.getMirrorStageId()), item.getSaasMark(), item.getDescription(), item.getProportion(), item.getExecutionEvent(),
                    item.getExecutor(), item.getCompletionCondition(), item.getDel());

            insertList.add(entity);
        }
        if (!insertList.isEmpty()) {
            stageWorkModel.insertBatch(insertList);
        }
    }

    /**
     * 校验高级版的业务规则的数量
     * @param corpid
     * @return true：不可以使用业务规则；false：可以使用
     * @author xingxing.xiao
     * @date 2020/06/15 15:55
     */
    private void checkProfessionalRules (String corpid, Long mirrorId) throws XbbException {
        // 当前业务规则数量
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put(StringConstant.CORPID, corpid);
        param.put(StringConstant.DEL, DelEnum.NORMAL.getDel());
        Integer currentCount = businessRuleInfoModel.getEntitysCount(param);

        // 镜像中业务规则数量
        param.clear();
        param.put("mirrorId", mirrorId);
        param.put(StringConstant.DEL, DelEnum.NORMAL.getDel());
        Integer mirrorRuleCount = mirrorBusinessRuleInfoModel.getEntitysCount(param);

        Integer total = currentCount + mirrorRuleCount;
        if (total > 5) {
            throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403048);
        }
    }

    /**
     * 提取业务规则并判断表单及字段
     * @param corpid
     * @param mirrorRuleList
     * @param sourceFormMirrorMap 原表单与镜像对应关系
     * @param sourceCorpid 用来创建镜像的公司id
     * @return 新旧表单对应关系
     * @throws
     * @author xingxing.xiao
     * @date 2020/05/13 15:49
     */
    private HashMap<Long, PaasFormEntity> checkBusinessRuleForm (String corpid, List<MirrorBusinessRuleInfoEntity> mirrorRuleList, Map<Long, Long> sourceFormMirrorMap, String sourceCorpid) throws XbbException {
        // 新旧表单对应关系
        HashMap<Long, PaasFormEntity> sourceToNewFormEntityMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        HashMap<Long, Long> sourceToNewFormIdMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);

        // 提取关联表单及字段
        HashMap<Long, HashSet<String>> workOrderFormIdToAttrMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        HashMap<Long, HashSet<String>> sysFormIdToAttrMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        HashMap<Long, HashSet<String>> cusFormIdToAttrMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        // businessType 与表单的对应关系
        HashMap<Integer, HashSet<Long>> sysBusinessTypeToFormIdsMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        HashMap<Long, Integer> sysFormToBusinessTypeMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        if (CollectionsUtil.isNotEmpty(mirrorRuleList)) {
            getTargetFormIdToAttrMap(mirrorRuleList, sourceFormMirrorMap, workOrderFormIdToAttrMap, sysFormIdToAttrMap, cusFormIdToAttrMap, sysBusinessTypeToFormIdsMap, sysFormToBusinessTypeMap);
        }

        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        // =========================== 校验表单 =========================
        List<Long> sourceFormIdIn = new ArrayList<>();
        if (cusFormIdToAttrMap.size() > 0) {
            sourceFormIdIn.addAll(cusFormIdToAttrMap.keySet());
        } else if (sysFormIdToAttrMap.size() > 0) {
            sourceFormIdIn.addAll(sysFormIdToAttrMap.keySet());
        } else if (workOrderFormIdToAttrMap.size() > 0) {
            sourceFormIdIn.addAll(workOrderFormIdToAttrMap.keySet());
        }
        if (sourceFormIdIn.size() > 0) {
            param.put(StringConstant.CORPID, corpid);
            param.put("sourceFormIdIn", sourceFormIdIn);
            param.put(StringConstant.DEL, DelEnum.NORMAL.getDel());
            // 加上正序，在后面多次安装的表单就会最新的formId覆盖旧的
            param.put("orderByStr", "id asc");
            List<FormInstallEntity> installFormList = formInstallModel.findEntitys(param);

            List<FormInstallEntity> cusInstallFormList = new ArrayList<>();
            List<FormInstallEntity> sysInstallFormList = new ArrayList<>();
            List<FormInstallEntity> workOrderInstallFormList = new ArrayList<>();
            installFormList.forEach(item -> {
                if (Objects.equals(item.getBusinessType(), XbbRefTypeEnum.WORK_ORDER.getCode())) {
                    workOrderInstallFormList.add(item);
                } else if (Objects.equals(item.getBusinessType(), BasicConstant.ZERO)) {
                    cusInstallFormList.add(item);
                } else{
                    sysInstallFormList.add(item);
                }
            });

            // 参数
            Set<Long> paasFormIdIn = new HashSet<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);

            // 处理自定义表单及字段
            if (cusFormIdToAttrMap.size() > 0) {
                if (cusInstallFormList.isEmpty()) {
                    // 任取一个表单做提示
                    throwErrorMessage(sourceFormIdIn.get(0), sourceCorpid);
                } else {
                    List<Long> formIdIn = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    HashSet<Long> installSourceFormIdIn = new HashSet<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    cusInstallFormList.forEach(item -> {
                        formIdIn.add(item.getFormId());
                        installSourceFormIdIn.add(item.getSourceFormId());
                    });

                    // 要保证每个都有，防止在安装表中没有查到
                    for (Long sourceFormId : cusFormIdToAttrMap.keySet()) {
                        if (!installSourceFormIdIn.contains(sourceFormId)) {
                            // 提示缺少的表单
                            throwErrorMessage(sourceFormId, sourceCorpid);
                        }
                    }
                    HashMap<Long, PaasFormEntity> newFormEntityMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    param.clear();
                    param.put(StringConstant.CORPID, corpid);
                    param.put("idIn", formIdIn);
                    param.put(StringConstant.DEL, DelEnum.NORMAL.getDel());
                    List<PaasFormEntity> paasformList = paasFormModel.findEntity(param);
                    paasformList.forEach(item -> {
                        newFormEntityMap.put(item.getId(), item);
                    });

                    // 取最新的formId 与 sourceFormId 匹配
                    cusInstallFormList.forEach(item -> {
                        if (newFormEntityMap.containsKey(item.getFormId())) {
                            PaasFormEntity formEntity = newFormEntityMap.get(item.getFormId());
                            sourceToNewFormEntityMap.put(item.getSourceFormId(), formEntity);
                            sourceToNewFormIdMap.put(item.getSourceFormId(), formEntity.getId());
                        }
                    });

                    // 判断是否有表单(此时对应关系已经匹配，要判断下关联的表单是否在匹配关系中)
                    for (FormInstallEntity item : cusInstallFormList) {
                        Long sourceFormId = item.getSourceFormId();
                        if (!sourceToNewFormEntityMap.containsKey(sourceFormId)) {
                            // 提示缺少的表单
                            throwErrorMessage(sourceFormId, sourceCorpid);
                        }
                    }
                }
                // 后面查询表单解释用
                paasFormIdIn.addAll(sourceToNewFormIdMap.values());
            }


            // 处理系统表单及字段
            if (sysFormIdToAttrMap.size() > 0) {

                Map<Long, Integer> sysInstallBusinessTypeMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                sysInstallFormList.forEach(item -> sysInstallBusinessTypeMap.put(item.getSourceFormId(), item.getBusinessType()));

                Map<Integer, PaasFormEntity> paasFormBusinessTypeMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                HashMap<Long, PaasFormEntity> newFormEntityMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);

                param.clear();
                param.put(StringConstant.CORPID, corpid);
                param.put("businessTypeList", sysBusinessTypeToFormIdsMap.keySet());
                param.put(StringConstant.DEL, DelEnum.NORMAL.getDel());
                // 加上倒序(多模板时，最小的formId覆盖大的formId)
                param.put("orderByStr", "id desc");
                List<PaasFormEntity> formEntityList = paasFormModel.findEntity(param);
                for (PaasFormEntity item : formEntityList) {
                    paasFormBusinessTypeMap.put(item.getBusinessType(), item);
                    newFormEntityMap.put(item.getId(), item);
                }

                // 看下关联的系统应用是否开启
                for (Integer businessType : sysBusinessTypeToFormIdsMap.keySet()) {
                    if (!paasFormBusinessTypeMap.containsKey(businessType)) {
                        // 若没查到系统表单，表明系统应用没开启(暂时只有进销存)
                        JXCMenuEnum byCode = JXCMenuEnum.getByCode(businessType);
                        if (Objects.nonNull(byCode)) {
                            throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403009, MirrorErrorCodeEnum.API_ERROR_403009.getMsg(), SystemAppMenuEnum.JXC.getName());
                        }
                    }
                }

                // 用安装过的sysInstallFormList 与 当前表单 newFormEntityMap.keySet() 作匹配，没匹配上的应用默认businessType表单
                for (FormInstallEntity entity : sysInstallFormList) {
                    if (newFormEntityMap.containsKey(entity.getFormId())) {
                        PaasFormEntity formEntity = newFormEntityMap.get(entity.getFormId());
                        sourceToNewFormEntityMap.put(entity.getSourceFormId(), formEntity);
                        sourceToNewFormIdMap.put(entity.getSourceFormId(), formEntity.getId());
                    } else {
                        // 用默认的
                        Integer businessType = entity.getBusinessType();
                        PaasFormEntity paasFormEntity = paasFormBusinessTypeMap.get(businessType);
                        sourceToNewFormEntityMap.put(entity.getSourceFormId(), paasFormEntity);
                        sourceToNewFormIdMap.put(entity.getSourceFormId(), paasFormEntity.getId());
                    }
                }

                // 用需要的 sysFormIdToAttrMap.keySet() 匹配
                for (Long sourceFormId : sysFormIdToAttrMap.keySet()) {
                    if (!sourceToNewFormIdMap.containsKey(sourceFormId)) {
                        // 用默认的
                        Integer businessType = sysFormToBusinessTypeMap.get(sourceFormId);
                        PaasFormEntity paasFormEntity = paasFormBusinessTypeMap.get(businessType);
                        sourceToNewFormEntityMap.put(sourceFormId, paasFormEntity);
                        sourceToNewFormIdMap.put(sourceFormId, paasFormEntity.getId());
                    }
                }
                // 后面查询表单解释用
                paasFormIdIn.addAll(sourceToNewFormIdMap.values());
            }

            // =============== 校验表单字段 =============
            HashMap<Long, HashSet<String>> formIdToExplainMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.clear();
            param.put(StringConstant.CORPID, corpid);
            param.put("formIdIn", paasFormIdIn);
            param.put(StringConstant.DEL, DelEnum.NORMAL.getDel());
            List<PaasFormExplainEntity> formExplainList = paasFormExplainModel.findEntitys(param);
            formExplainList.forEach(item -> {
                Long formId = item.getFormId();
                List<FieldAttrEntity> fieldAttrEntities = JSON.parseArray(item.getExplains(), FieldAttrEntity.class);
                if (formIdToExplainMap.containsKey(formId)) {
                    HashSet<String> attrList = formIdToExplainMap.get(formId);
                    getFormExplainAttrs(fieldAttrEntities, attrList);
                    formIdToExplainMap.put(item.getFormId(), attrList);
                } else {
                    HashSet<String> attrList = new HashSet<>();
                    getFormExplainAttrs(fieldAttrEntities, attrList);
                    formIdToExplainMap.put(item.getFormId(), attrList);
                }
            });

            if (cusFormIdToAttrMap.size() > 0) {
                for (Map.Entry<Long, HashSet<String>> entry : cusFormIdToAttrMap.entrySet()) {
                    Long formId = sourceToNewFormIdMap.get(entry.getKey());
                    HashSet<String> nowAttrs = formIdToExplainMap.get(formId);
                    HashSet<String> srcAttrs = entry.getValue();
                    if (!nowAttrs.containsAll(srcAttrs)) {
                        // 查询缺少字段的是哪个表单
                        throwErrorMessage(entry.getKey(), sourceCorpid);
                    }
                }
            }

            if (sysFormIdToAttrMap.size() > 0) {
                for (Map.Entry<Long, HashSet<String>> entry : sysFormIdToAttrMap.entrySet()) {
                    Long formId = sourceToNewFormIdMap.get(entry.getKey());
                    HashSet<String> nowAttrs = formIdToExplainMap.get(formId);
                    HashSet<String> srcAttrs = entry.getValue();
                    if (!nowAttrs.containsAll(srcAttrs)) {
                        // 查询对应的系统表单
                        PaasFormEntity formEntity = sourceToNewFormEntityMap.get(entry.getKey());
                        if (Objects.nonNull(formEntity)) {
                            throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403032, MirrorErrorCodeEnum.API_ERROR_403032.getMsg(), formEntity.getName());
                        } else {
                            throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403032, MirrorErrorCodeEnum.API_ERROR_403032.getMsg(), entry.getKey());
                        }
                    }
                }
            }

            // ============= 处理工单表单及字段(业务规则没有工单，暂不处理)
            /*if (workOrderFormIdToAttrMap.size() > 0) {
                List<Long> formIdIn = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                workOrderInstallFormList.forEach(item -> formIdIn.add(item.getFormId()));

                // 查询默认工单模板，备用
                param.put("columns", "id, name, is_default, enable");
                param.put("corpid", corpid);
                param.put("isDefault", 1);
                param.put("del", DelEnum.NORMAL.getDel());
                List<WorkOrderTemplateEntity> templateEntityList = workOrderTemplateModel.findEntity(param);
                if (CollectionUtils.isEmpty(templateEntityList)) {
                    // 工单默认模板 不能删除，如果没查到表单说明工单应用没开启
                    throw new XbbException(ErrorCodeEnum.API_ERROR_403009, ErrorCodeEnum.API_ERROR_403009.getMsg(), SystemAppMenuEnum.WORK_ORDER.getName());
                }

                HashMap<Long, WorkOrderFormEntity> newFormEntityMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                WorkOrderFormEntity defaultFormEntity = new WorkOrderFormEntity();
                param.clear();
                param.put(StringConstant.CORPID, corpid);
                param.put("idIn", formIdIn);
                param.put(StringConstant.DEL, DelEnum.NORMAL.getDel());
                List<WorkOrderFormEntity> workOrderFormList = workOrderFormModel.findEntitys(param);
                for (WorkOrderFormEntity entity : workOrderFormList) {
                    if (Objects.equals(entity.getTemplateId(), templateEntityList.get(0).getId())) {
                        defaultFormEntity = entity;
                    }
                    newFormEntityMap.put(entity.getId(), entity);
                }

                // 取最新的formId 与 sourceFormId 匹配
                for (FormInstallEntity item : workOrderInstallFormList) {
                    if (newFormEntityMap.keySet().contains(item.getFormId())) {
                        WorkOrderFormEntity workOrderFormEntity = newFormEntityMap.get(item.getFormId());
                        PaasFormEntity formEntity = new PaasFormEntity();
                        BeanUtil.copyProperties(workOrderFormEntity, formEntity);
                        sourceToNewFormEntityMap.put(item.getSourceFormId(), formEntity);
                        sourceToNewFormIdMap.put(item.getSourceFormId(), formEntity.getId());
                    } else {
                        // 对应默认工单
                        PaasFormEntity formEntity = new PaasFormEntity();
                        BeanUtil.copyProperties(defaultFormEntity, formEntity);
                        sourceToNewFormEntityMap.put(item.getSourceFormId(), formEntity);
                        sourceToNewFormIdMap.put(item.getSourceFormId(), formEntity.getId());
                    }
                }

                for (Long sourceFormId : workOrderFormIdToAttrMap.keySet()) {
                    if (!sourceToNewFormIdMap.containsKey(sourceFormId)) {
                        // 对应默认工单
                        PaasFormEntity formEntity = new PaasFormEntity();
                        BeanUtil.copyProperties(defaultFormEntity, formEntity);
                        sourceToNewFormEntityMap.put(sourceFormId, formEntity);
                        sourceToNewFormIdMap.put(sourceFormId, formEntity.getId());
                    }
                }

                // 校验工单字段
                param.clear();
                param.put(StringConstant.CORPID, corpid);
                param.put("formIdIn", sourceToNewFormIdMap.values());
                param.put(StringConstant.DEL, DelEnum.NORMAL.getDel());
                List<WorkOrderExplainEntity> workOrderExplainList = workOrderExplainModel.findEntitys(param);
                HashMap<Long, HashSet<String>> formExplainMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                workOrderExplainList.forEach(item -> {
                    Long formId = item.getFormId();
                    List<FieldAttrEntity> fieldAttrEntities = JSON.parseArray(item.getExplains(), FieldAttrEntity.class);
                    if (formExplainMap.containsKey(formId)) {
                        HashSet<String> attrList = formExplainMap.get(formId);
                        getFormExplainAttrs(fieldAttrEntities, attrList);
                        formExplainMap.put(item.getFormId(), attrList);
                    } else {
                        HashSet<String> attrList = new HashSet<>();
                        getFormExplainAttrs(fieldAttrEntities, attrList);
                        formExplainMap.put(item.getFormId(), attrList);
                    }
                });

                for (Map.Entry<Long, HashSet<String>> entry : workOrderFormIdToAttrMap.entrySet()) {
                    Long formId = sourceToNewFormIdMap.get(entry.getKey());
                    HashSet<String> nowAttrs = formIdToExplainMap.get(formId);
                    HashSet<String> srcAttrs = entry.getValue();
                    if (!nowAttrs.containsAll(srcAttrs)) {
                        // 查询对应的工单表单
                        PaasFormEntity formEntity = sourceToNewFormEntityMap.get(entry.getKey());
                        throw new XbbException(ErrorCodeEnum.API_ERROR_403042, String.format(ErrorCodeEnum.API_ERROR_403042.getMsg(), formEntity.getName()));
                    }
                }
            }*/
        }

        return sourceToNewFormEntityMap;
    }

    /**
     * 提示缺少的表单
     * @param sourceFormId
     * @param sourceCorpid
     * @throws XbbException
     */
    private void throwErrorMessage (Long sourceFormId, String sourceCorpid) throws XbbException {
        PaasFormEntityExt formEntity = paasFormModel.getByKey(sourceFormId, sourceCorpid);
        if (Objects.nonNull(formEntity)) {
            throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403032, MirrorErrorCodeEnum.API_ERROR_403032.getMsg(), formEntity.getName());
        } else {
            throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403032, MirrorErrorCodeEnum.API_ERROR_403032.getMsg(), sourceFormId);
        }
    }

    /**
     * 获取字段
     * 注：子表单的字段做下拼接
     * @param explainList
     * @param attrList
     * @author xingxing.xiao
     * @date 2020/05/14 11:24
     */
    private void getFormExplainAttrs(List<FieldAttrEntity> explainList, HashSet<String> attrList) {
        for (FieldAttrEntity attrEntity : explainList) {
            if (Objects.nonNull(attrEntity.getSubForm())) {
                // 子表单
                List<? extends FieldAttrEntity> subExplainList = attrEntity.getSubForm().getItems();
                String attr = attrEntity.getAttr();
                // 加上判空，例： 退货退款表单里的退货产品字段
                if (Objects.nonNull(subExplainList)) {
                    for (FieldAttrEntity subAttrEntity : subExplainList) {
                        attrList.add(attr + "." + subAttrEntity.getAttr());
                    }
                }
            } else {
                attrList.add(attrEntity.getAttr());
            }
        }
    }

    /**
     * 找出关联的表单及字段
     * @param mirrorRuleList 业务规则
     * @param sourceFormMirrorMap  原表单与镜像对应关系
     * @param workOrderFormIdToAttrMap 工单表单及字段，要返回
     * @param sysFormIdToAttrMap 系统表单及字段
     * @param cusFormIdToAttrMap 自定义表单及字段
     * @param sysBusinessTypeToFormIdsMap businessType 与表单的对应关系
     * @param sysFormToBusinessTypeMap
     * @author xingxing.xiao
     * @date 2020/05/12 15:49
     */
    public void getTargetFormIdToAttrMap(List<MirrorBusinessRuleInfoEntity> mirrorRuleList, Map<Long, Long> sourceFormMirrorMap, HashMap<Long, HashSet<String>> workOrderFormIdToAttrMap, HashMap<Long, HashSet<String>> sysFormIdToAttrMap, HashMap<Long, HashSet<String>> cusFormIdToAttrMap, HashMap<Integer, HashSet<Long>> sysBusinessTypeToFormIdsMap, HashMap<Long, Integer> sysFormToBusinessTypeMap) throws XbbException {
        for (MirrorBusinessRuleInfoEntity ruleInfoEntity : mirrorRuleList) {
            // 生效规则
            if (StringUtil.isNotEmpty(ruleInfoEntity.getActiveName())) {
                if (!sourceFormMirrorMap.containsKey(ruleInfoEntity.getActiveFormId())) {
                    // 不是同一个镜像里的表单要做处理（因为同一个镜像里的表单可直接替换）

                    // 目标表单
                    Long activeFormId = ruleInfoEntity.getActiveFormId();
                    Integer activeSaasMark = ruleInfoEntity.getActiveSaasMark();
                    Integer activeBusinessType = ruleInfoEntity.getActiveBusinessType();
                    Integer activeType = ruleInfoEntity.getActiveType();
                    // 过滤条件
                    List<FilterConditionsPoJo> filterConditions = JSONArray.parseArray(ruleInfoEntity.getActiveFilterConditions(), FilterConditionsPoJo.class);
                    // 操作方式
                    List<OperationsPojo> operations = JSONArray.parseArray(ruleInfoEntity.getActiveOperations(), OperationsPojo.class);
                    // 提取字段
                    getRuleLinkAttrList(activeFormId, activeSaasMark, activeBusinessType, activeType, filterConditions, operations, workOrderFormIdToAttrMap, sysFormIdToAttrMap, cusFormIdToAttrMap, sysBusinessTypeToFormIdsMap, sysFormToBusinessTypeMap);
                }
            }

            // 失效规则
            if (StringUtil.isNotEmpty(ruleInfoEntity.getInactiveName())) {
                if (!sourceFormMirrorMap.containsKey(ruleInfoEntity.getInactiveFormId())) {
                    // 不是同一个镜像里的表单要做处理（因为同一个镜像里的表单可直接替换）

                    // 目标表单
                    Long inactiveFormId = ruleInfoEntity.getInactiveFormId();
                    Integer inactiveSaasMark = ruleInfoEntity.getInactiveSaasMark();
                    Integer inactiveBusinessType = ruleInfoEntity.getInactiveBusinessType();
                    Integer inactiveType = ruleInfoEntity.getInactiveType();
                    List<FilterConditionsPoJo> filterConditionsPoJos = JSONArray.parseArray(ruleInfoEntity.getInactiveFilterConditions(), FilterConditionsPoJo.class);
                    List<OperationsPojo> operationsPojos = JSONArray.parseArray(ruleInfoEntity.getInactiveOperations(), OperationsPojo.class);
                    // 提取字段
                    getRuleLinkAttrList(inactiveFormId, inactiveSaasMark, inactiveBusinessType, inactiveType, filterConditionsPoJos, operationsPojos, workOrderFormIdToAttrMap, sysFormIdToAttrMap, cusFormIdToAttrMap, sysBusinessTypeToFormIdsMap, sysFormToBusinessTypeMap);
                }
            }
        }
    }

    /**
     * 提取字段
     * @param ruleType 高级规则(公式) or 简易规则
     * @param workOrderFormIdToAttrMap 工单表单及字段，要返回
     * @param sysFormIdToAttrMap 系统表单及字段
     * @param cusFormIdToAttrMap 自定义表单及字段
     * @param sysBusinessTypeToFormIdsMap businessType 与表单的对应关系
     * @param sysFormToBusinessTypeMap 表单与businessType对应关系
     * @author xingxing.xiao
     * @date 2020/05/13 15:25
     */
    private void getRuleLinkAttrList(Long targetFormId, Integer saasMark, Integer businessType, Integer ruleType, List<FilterConditionsPoJo> filterConditions, List<OperationsPojo> operations, HashMap<Long, HashSet<String>> workOrderFormIdToAttrMap, HashMap<Long, HashSet<String>> sysFormIdToAttrMap, HashMap<Long, HashSet<String>> cusFormIdToAttrMap, HashMap<Integer, HashSet<Long>> sysBusinessTypeToFormIdsMap, HashMap<Long, Integer> sysFormToBusinessTypeMap) throws XbbException {

        if (Objects.equals(saasMark, SaasMarkEnum.SAAS.getCode())) {
            if (Objects.equals(XbbRefTypeEnum.WORK_ORDER.getCode(), businessType)) {
                if (workOrderFormIdToAttrMap.containsKey(targetFormId)) {
                    HashSet<String> attrList = workOrderFormIdToAttrMap.get(targetFormId);
                    setRuleLinkAttrList(workOrderFormIdToAttrMap, targetFormId, attrList, filterConditions, operations, ruleType);
                } else {
                    HashSet<String> attrList = new HashSet<>();
                    setRuleLinkAttrList(workOrderFormIdToAttrMap, targetFormId, attrList, filterConditions, operations, ruleType);
                }
            } else {
                if (sysFormIdToAttrMap.containsKey(targetFormId)) {
                    HashSet<String> attrList = sysFormIdToAttrMap.get(targetFormId);
                    setRuleLinkAttrList(sysFormIdToAttrMap, targetFormId, attrList, filterConditions, operations, ruleType);
                } else {
                    HashSet<String> attrList = new HashSet<>();
                    setRuleLinkAttrList(sysFormIdToAttrMap, targetFormId, attrList, filterConditions, operations, ruleType);
                }

                // 系统表单记录 businessType 与表单的对应关系
                if (sysBusinessTypeToFormIdsMap.containsKey(businessType)) {
                    HashSet<Long> formIds = sysBusinessTypeToFormIdsMap.get(businessType);
                    formIds.add(targetFormId);
                    sysBusinessTypeToFormIdsMap.put(businessType, formIds);
                } else {
                    HashSet<Long> formIds = new HashSet<>();
                    formIds.add(targetFormId);
                    sysBusinessTypeToFormIdsMap.put(businessType, formIds);
                }
                // 表单与businessType
                sysFormToBusinessTypeMap.put(targetFormId, businessType);
            }
        } else {
            if (cusFormIdToAttrMap.containsKey(targetFormId)) {
                HashSet<String> attrList = cusFormIdToAttrMap.get(targetFormId);
                setRuleLinkAttrList(cusFormIdToAttrMap, targetFormId, attrList, filterConditions, operations, ruleType);
            } else {
                HashSet<String> attrList = new HashSet<>();
                setRuleLinkAttrList(cusFormIdToAttrMap, targetFormId, attrList, filterConditions, operations, ruleType);
            }
        }
    }

    /**
     *
     * @param formIdToAttrMap 表单及字段对应关系
     * @param targetFormId 目标表单
     * @param filterConditions 过滤条件
     * @param operations 操作方式
     * @param ruleType 高级规则(公式) or 简易规则
     * @author xingxing.xiao
     * @date 2020/05/13 15:25
     */
    private void setRuleLinkAttrList(HashMap<Long, HashSet<String>> formIdToAttrMap, Long targetFormId, HashSet<String> attrList, List<FilterConditionsPoJo> filterConditions, List<OperationsPojo> operations, Integer ruleType) throws XbbException {
        for (FilterConditionsPoJo filterCondition : filterConditions) {
            String attr = filterCondition.getAttr();
            if (StringUtil.isEmpty(attr)) {
                continue;
            }
            // 所选数据(即：主键)不处理
            if (Objects.equals(attr, FieldTypeEnum.DATAID.getAlias())) {
                continue;
            }
            // 注： 若是子表单，格式: subForm_1.text_1
            attrList.add(attr);
        }

        // 操作方式
        for (OperationsPojo operation : operations) {
            if (Objects.equals(ruleType, RuleVersionEnum.ADVANCED.getType())) {
                // 解析公式(e.g. ADD({self.subForm_1.num_3},MULTIPLY({12298.subForm_1.num_1},{12298.subForm_1.num_2})) )，参考：ComplexFormatHelp.java -> complexFormat()
                String formula = operation.getFormulaInfo().getFormula();
                String[] formulaArray = RegexUtil.getFormulaAttr(formula);
                for (String formulaAttr : formulaArray) {
                    String attr;
                    if (RegexUtil.isStartWithNumber(formulaAttr)) {
                        int startNum = formulaAttr.indexOf(".") + 1;
                        int length = formulaAttr.length();
                        attr = formulaAttr.substring(startNum, length).replaceAll("[\\{\\}]", "");
                        attrList.add(attr);
                    } else if (formulaAttr.contains("user.")) {
                        throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403043, MirrorErrorCodeEnum.API_ERROR_403043.getMsg(), I18nMessageUtil.getMessage(CommonConstant.EMPLOYEE));
                    } else if (formulaAttr.contains("dept.")) {
                        throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403043, MirrorErrorCodeEnum.API_ERROR_403043.getMsg(), I18nMessageUtil.getMessage(CommonConstant.DEPARTMENT));
                    }
                }
            } else {
                attrList.add(operation.getAttr());
            }
        }
        formIdToAttrMap.put(targetFormId, attrList);
    }

    /**
     * 记录镜像应用安装信息
     * @param formList
     * @param corpid
     * @param appId
     * @param sourAppId
     * @param sourceCorpid
     * @param creatorId
     * @param mirrorId
     * @param mirrorApplicationType
     * @param retainAppId 为0时表示没有系统表单
     * @author xingxing.xiao
     */
    private Long recordAppInstall(List<PaasFormEntity> formList, Map<Long, Long> paasMenuMap, Map<Long, Long> mirrorMenuMap, Map<Long, Long> mirrorFormMap, String corpid, Long appId, Long sourAppId, String sourceCorpid, String creatorId, Long mirrorId, Integer mirrorApplicationType, Long retainAppId) throws XbbException {
        Long appInstallId = 0L;
        long now = DateUtil.getInt();

        Integer isToInit = 0;
        Integer isToPrevious = 0;
        if (Objects.equals(mirrorApplicationType,MirrorApplicationTypeEnum.SYSTEM.getCode())){
            isToInit = MirrorRollBackTypeEnum.NO_ROLL_BACK_TO_INIT.getFlag();
            isToPrevious = MirrorRollBackTypeEnum.NO_ROLL_BACK_TO_PREVIOUS.getFlag();
        } else if (Objects.equals(mirrorApplicationType,MirrorApplicationTypeEnum.CUSTOM.getCode())){
            isToInit = MirrorRollBackTypeEnum.NO_ROLL_BACK.getFlag();
            isToPrevious = MirrorRollBackTypeEnum.NO_ROLL_BACK.getFlag();
        }
        AppInstallEntity installEntity = new AppInstallEntity(corpid, AppInstallTypeEnum.MIRROR.getType(), appId, sourAppId, sourceCorpid, creatorId, mirrorId, MirrorTypeEnum.APPLICATION.getCode(), mirrorApplicationType, isToInit, isToPrevious);
        if (Objects.equals(mirrorApplicationType,MirrorApplicationTypeEnum.SYSTEM.getCode())){
            installEntity.setIsBack(MirrorRollBackTypeEnum.OPERATE_BACK.getFlag());
            if (Objects.equals(retainAppId, 0L)) {
                installEntity.setIsBack(MirrorRollBackTypeEnum.NO_OPERATE_BACK_FOR_SYSTEM_APP.getFlag());
            }
        }

        JSONArray formIds = new JSONArray();
        JSONArray sourceFormIds = new JSONArray();
        // 记录表单安装信息
        if (!formList.isEmpty()) {
            if (Objects.equals(mirrorApplicationType,MirrorApplicationTypeEnum.SYSTEM.getCode())) {
                // 更新同一个应用下的前一个表单镜像的操作状态 (不可操作回退)
                updatePreviewInstall(corpid, appId, mirrorApplicationType);
            }
            List<FormInstallEntity> forminstallList = new ArrayList<>();
            formList.forEach(item -> {
                Long formId = item.getId();
                Long sourceFormId = mirrorFormMap.get(item.getMirrorFormId());
                formIds.add(formId);
                sourceFormIds.add(sourceFormId);
                Long sourceMenuId = mirrorMenuMap.get(item.getMirrorMenuId());
                Long menuId = paasMenuMap.get(item.getMirrorMenuId());
                FormInstallEntity entity = new FormInstallEntity(corpid, AppInstallTypeEnum.MIRROR.getType(), mirrorId, null, appId, menuId, formId, sourAppId, sourceMenuId, sourceFormId, sourceCorpid, item.getBusinessType(), item.getDel());
                entity.setCreatorId(creatorId);
                entity.setAddTime(now);
                entity.setUpdateTime(now);
                forminstallList.add(entity);
            });
            installEntity.setFormIds(formIds.toJSONString());
            installEntity.setSourceFormIds(sourceFormIds.toJSONString());
            appInstallModel.insert(installEntity);
            appInstallId = installEntity.getId();
            forminstallList.forEach(item -> item.setAppInstallId(installEntity.getId()));
            formInstallModel.insertBatch(forminstallList);
        }
        return appInstallId;
    }

    /**
     * 更新同一个应用下的前一个表单镜像的操作状态 (不可操作回退)
     * @param corpid
     * @param appId
     * @param mirrorApplicationType
     * @throws XbbException
     * @author xingxing.xiao
     */
    private void updatePreviewInstall(String corpid, Long appId, Integer mirrorApplicationType) throws XbbException {
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("columns", "id, corpid, app_id, is_to_init, is_to_previous, is_back");
        param.put("corpid", corpid);
        param.put("appId", appId);
        param.put("mirrorApplicationType", mirrorApplicationType);
        param.put("del", DelEnum.NORMAL.getDel());
        param.put("orderByStr", "id desc");
        List<AppInstallEntity> installEntities = appInstallModel.findEntitys(param);
        if (installEntities.size() > 0) {
            AppInstallEntity appInstallEntity = installEntities.get(0);
            appInstallEntity.setIsBack(MirrorRollBackTypeEnum.NO_OPERATE_BACK.getFlag());
            appInstallModel.update(appInstallEntity);
        }
    }

    /**
     *  判断系统表单是否有数据（还有流程表单数据）
     * @author xingxing.xiao
     * @return true 有数据   false 无数据
     */
    private boolean checkFormData (List<PaasFormEntity> paasformList, String corpid, Long appId) throws XbbException {
        try {
            // 客户和合同是多模板，客户有回收站，单独提出来判断
            Set<Long> customerFormIdSet = new HashSet<>();
            Set<Long> contractFormIdSet = new HashSet<>();
            Set<Long> opportunityFormIdSet = new HashSet<>();
            List<PaasFormEntity> otherFormList = new ArrayList<>();
            Set<Long> formIdSet = new HashSet<>();
            for (PaasFormEntity formEntity : paasformList) {
                if (Objects.equals(formEntity.getBusinessType(), XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode())) {
                    customerFormIdSet.add(formEntity.getId());
                } else if (Objects.equals(formEntity.getBusinessType(), XbbRefTypeEnum.CONTRACT.getCode())) {
                    contractFormIdSet.add(formEntity.getId());
                } else if (Objects.equals(formEntity.getBusinessType(), XbbRefTypeEnum.SALES_OPPORTUNITY.getCode())) {
                    opportunityFormIdSet.add(formEntity.getId());
                } else {
                    otherFormList.add(formEntity);
                }
                formIdSet.add(formEntity.getId());
            }
            // 客户表单数据
            if (!customerFormIdSet.isEmpty()) {
                IndexTypeEnum customerIndex = indexTypeModel.getCustomerIndex(corpid, XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode(), SaasMarkEnum.SAAS.getCode());

                BoolQueryBuilder boolQueryBuilder = boolQuery();
                SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
                boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
                boolQueryBuilder.filter(termsQuery("formId", customerFormIdSet));
                // 回收站的客户数据要考虑
                boolQueryBuilder.filter(termsQuery("del", Arrays.asList(DelEnum.NORMAL.getDel(), DelEnum.DELETE.getDel())));
                boolQueryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD,customerIndex.getType()));
                sourceBuilder.query(boolQueryBuilder);
                SearchRequest searchRequest = new SearchRequest(customerIndex.getIndex());
                searchRequest.source(sourceBuilder);
                long count = xbbElasticsearchRestTemplate.count(searchRequest, PaasFormDataEntityExt.class);
                if (count > 0) {
                    throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403003, MirrorErrorCodeEnum.API_ERROR_403003.getMsg(), XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getName());
                }
            }
            // 合同多表单数据
            if (!contractFormIdSet.isEmpty()) {
                IndexTypeEnum indexTypeEnum = IndexTypeEnum.IDX_SAAS_CONTRACT;
                BoolQueryBuilder boolQueryBuilder = boolQuery();
                SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
                boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
                boolQueryBuilder.filter(termsQuery("formId", contractFormIdSet));
                boolQueryBuilder.filter(termQuery("del", DelEnum.NORMAL.getDel()));
                boolQueryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD,indexTypeEnum.getType()));
                sourceBuilder.query(boolQueryBuilder);
                SearchRequest searchRequest = new SearchRequest(indexTypeEnum.getIndex());
                searchRequest.source(sourceBuilder);
                long count = xbbElasticsearchRestTemplate.count(searchRequest, PaasFormDataEntityExt.class);
                if (count > 0) {
                    throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403003, MirrorErrorCodeEnum.API_ERROR_403003.getMsg(), XbbRefTypeEnum.CONTRACT.getName());
                }
            }
            // 机会多模板数据
            if (!opportunityFormIdSet.isEmpty()) {
                IndexTypeEnum indexTypeEnum = IndexTypeEnum.IDX_SAAS_OPPORTUNITY;
                BoolQueryBuilder boolQueryBuilder = boolQuery();
                SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
                boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
                boolQueryBuilder.filter(termsQuery("formId", opportunityFormIdSet));
                boolQueryBuilder.filter(termQuery("del", DelEnum.NORMAL.getDel()));
                boolQueryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD,indexTypeEnum.getType()));
                sourceBuilder.query(boolQueryBuilder);
                SearchRequest searchRequest = new SearchRequest(indexTypeEnum.getIndex());
                searchRequest.source(sourceBuilder);
                long count = xbbElasticsearchRestTemplate.count(searchRequest, PaasFormDataEntityExt.class);
                if (count > 0) {
                    throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403003, MirrorErrorCodeEnum.API_ERROR_403003.getMsg(), XbbRefTypeEnum.SALES_OPPORTUNITY.getName());
                }
            }

            // 其他表单数据
            for (PaasFormEntity formEntity : otherFormList) {
                Integer businessType = formEntity.getBusinessType();
                IndexTypeEnum indexTypeEnum = indexTypeModel.getIndexType(corpid, businessType, formEntity.getSaasMark());
                if (!Objects.equals(indexTypeEnum, IndexTypeEnum.UNKNOWN)) {
                    BoolQueryBuilder boolQueryBuilder = boolQuery();
                    SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
                    boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
                    boolQueryBuilder.filter(termQuery("formId", formEntity.getId()));
                    boolQueryBuilder.filter(termQuery("del", DelEnum.NORMAL.getDel()));
                    if (Objects.equals(XbbRefTypeEnum.CUSTOMER_COMMUNICATE.getCode(), businessType)) {
                        boolQueryBuilder.filter(termQuery("logType", DelEnum.NORMAL.getDel()));
                    }
                    boolQueryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD,indexTypeEnum.getType()));
                    sourceBuilder.query(boolQueryBuilder);
                    SearchRequest searchRequest = new SearchRequest(indexTypeEnum.getIndex());
                    searchRequest.source(sourceBuilder);
                    long count = xbbElasticsearchRestTemplate.count(searchRequest, PaasFormDataEntityExt.class);
                    if (count > 0) {
                        throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403003, MirrorErrorCodeEnum.API_ERROR_403003.getMsg(), XbbRefTypeEnum.getByCode(businessType).getName());
                    }
                }
            }

            // 流程表单数据
            Map<String, Object> params = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            params.put("corpid", corpid);
            params.put("appId", appId);
            params.put("formIdIn", formIdSet);
            params.put("del", DelEnum.NORMAL.getDel());
            Integer taskCount = paasProcessTaskModel.getEntitysCount(params);
            if (taskCount > 0) {
                throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403041, MirrorErrorCodeEnum.API_ERROR_403041.getMsg());
            }
        } catch (XbbException e){
            LOG.error("MirrorServiceImpl.checkFormData error", e);
            throw e;
        } catch (Exception e) {
            LOG.error("MirrorServiceImpl.checkFormData  出错", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
        }
        return false;
    }

    /**
     * 系统要覆盖的表单（包含菜单和表单，可以在此处记录回退状态）
     * 注：原应用id、菜单id、表单id等不变
     *
     * @param mirrorSystemMenuList 系统菜单镜像
     * @param mirrorSystemFormList  系统表单镜像
     * @param paasMenuMap  记录镜像与新菜单表单等对应关系
     * @param paasMenuList
     * @param paasformList
     * @param corpid
     * @param paasAppEntity
     * @param installFormList  记录安装后的表单，后面要用到
     * @return retainAppId 应用备份主键id
     * @author xingxing.xiao
     */
    private Long coverForm(List<MirrorMenuEntity> mirrorSystemMenuList, List<MirrorFormEntity> mirrorSystemFormList, Map<Long, Long> paasMenuMap, List<PaasMenuEntity> paasMenuList, List<PaasFormEntity> paasformList, String corpid,PaasAppEntity paasAppEntity, String creatorId, List<PaasFormEntity> installFormList) throws XbbException {
        //备份系统应用
        PaasAppRetainEntity paasAppRetainEntity = new PaasAppRetainEntity(paasAppEntity.getId(), corpid, paasAppEntity.getName(), paasAppEntity.getIcon(), paasAppEntity.getColor(), paasAppEntity.getSaasMark(), paasAppEntity.getSort(), paasAppEntity.getCreatorId(), paasAppEntity.getAddTime(), paasAppEntity.getUpdateTime(), paasAppEntity.getDel(), paasAppEntity.getAlias(), paasAppEntity.getEnable(), paasAppEntity.getDisplay());
        paasAppRetainModel.insert(paasAppRetainEntity);
        Long retainAppId = paasAppRetainEntity.getId();

        //备份系统菜单
        List<PaasMenuRetainEntity> paasMenuRetainEntityList = new ArrayList<>();
        for (PaasMenuEntity paasMenuEntity : paasMenuList) {
            PaasMenuRetainEntity paasMenuRetainEntity = new PaasMenuRetainEntity(paasMenuEntity);
            paasMenuRetainEntity.setRetainAppId(retainAppId);
            paasMenuRetainEntityList.add(paasMenuRetainEntity);
        }
        paasMenuRetainModel.insertBatch(paasMenuRetainEntityList,corpid);

        long now = DateUtil.getInt();
        // 1. 批量更新系统菜单
        // 记录对应关系（根据alias找对应菜单）
        Map<String, Long> mirrorMenuAliasMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<Long, PaasMenuEntity> paasMenuEntityMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        // 系统菜单不能删除，应该替换就好了
        mirrorSystemMenuList.forEach(item -> mirrorMenuAliasMap.put(item.getAlias(), item.getId()));
        paasMenuList.forEach(item -> {
            Long mirrorMenuId = mirrorMenuAliasMap.get(item.getAlias());
            paasMenuMap.put(mirrorMenuId, item.getId());
            paasMenuEntityMap.put(mirrorMenuId, item);
        });
        List<PaasMenuEntity> paasMenuEntities = new ArrayList<>();
        for (MirrorMenuEntity entity : mirrorSystemMenuList) {
            // id 取原菜单id
            PaasMenuEntity paasMenuEntity = paasMenuEntityMap.get(entity.getId());
            if (Objects.isNull(paasMenuEntity)) {
                throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403050, String.format(MirrorErrorCodeEnum.API_ERROR_403050.getMsg(), entity.getName()));
            }
            paasMenuEntity.setName(entity.getName());
            paasMenuEntity.setIcon(entity.getIcon());
            paasMenuEntity.setColor(entity.getColor());
            paasMenuEntity.setType(entity.getType());
            paasMenuEntity.setEnable(entity.getEnable());
            paasMenuEntity.setUrl(entity.getUrl());
            paasMenuEntity.setMobileUrl(entity.getMobileUrl());
            paasMenuEntity.setUpdateTime(now);
            paasMenuEntity.setDel(entity.getDel());
            paasMenuEntities.add(paasMenuEntity);
        }
        if (!paasMenuEntities.isEmpty()) {
            paasMenuModel.updateBatch(paasMenuEntities, corpid);
        }

        if (!paasformList.isEmpty()) {
            //备份系统表单
            List<PaasFormRetainEnity> paasFormRetainEnityList = new ArrayList<>();
            paasformList.forEach(item->{
                PaasFormRetainEnity paasFormRetainEnity = new PaasFormRetainEnity(item);
                paasFormRetainEnity.setRetainAppId(retainAppId);
                paasFormRetainEnityList.add(paasFormRetainEnity);
            });
            paasFormRetainModel.insertBatch(paasFormRetainEnityList,corpid);

            // 2. 批量更新系统表单
            // 客户、合同多表单单独处理，其他根据businessType找镜像表单与该公司表单的一一对应关系
            List<MirrorFormEntity> customerMirrorFormList = new ArrayList<>();
            List<MirrorFormEntity> contractMirrorFormList = new ArrayList<>();
            List<MirrorFormEntity> opportunityMirrorFormList = new ArrayList<>();
            List<MirrorFormEntity> clueMirrorFormList = new ArrayList<>();
            List<MirrorFormEntity> workOrderV2MirrorFormList = new ArrayList<>();
            List<MirrorFormEntity> receiptOrderMirrorFormList = new ArrayList<>();
            List<MirrorFormEntity> otherMirrorFormList = new ArrayList<>();
            for (MirrorFormEntity mirrorFormEntity : mirrorSystemFormList) {
                if (Objects.equals(mirrorFormEntity.getBusinessType(), XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode())) {
                    customerMirrorFormList.add(mirrorFormEntity);
                } else if (Objects.equals(mirrorFormEntity.getBusinessType(), XbbRefTypeEnum.CONTRACT.getCode())) {
                    contractMirrorFormList.add(mirrorFormEntity);
                } else if (Objects.equals(mirrorFormEntity.getBusinessType(), XbbRefTypeEnum.SALES_OPPORTUNITY.getCode())) {
                    opportunityMirrorFormList.add(mirrorFormEntity);
                }  else if (Objects.equals(mirrorFormEntity.getBusinessType(), XbbRefTypeEnum.CLUE.getCode())){
                    clueMirrorFormList.add(mirrorFormEntity);
                } else if (Objects.equals(mirrorFormEntity.getBusinessType(),XbbRefTypeEnum.WORK_ORDER_V2.getCode())) {
                    // 工单v2支持多模板
                    workOrderV2MirrorFormList.add(mirrorFormEntity);
                } else if (Objects.equals(mirrorFormEntity.getBusinessType(),XbbRefTypeEnum.RECEIPT_ORDER.getCode())) {
                    // 回执单支持多模板
                    receiptOrderMirrorFormList.add(mirrorFormEntity);
                } else {
                    otherMirrorFormList.add(mirrorFormEntity);
                }
            }
            List<PaasFormEntity> customerFormList = new ArrayList<>();
            List<PaasFormEntity> contractFormList = new ArrayList<>();
            List<PaasFormEntity> opportunityFormList = new ArrayList<>();
            List<PaasFormEntity> clueFormList = new ArrayList<>();
            List<PaasFormEntity> workOrderV2FormList = new ArrayList<>();
            List<PaasFormEntity> receiptOrderFormList = new ArrayList<>();
            List<PaasFormEntity> otherFormList = new ArrayList<>();
            List<Long> customerFormIdIn = new ArrayList<>();
            List<Long> clueFormIdIn = new ArrayList<>();
            List<Long> opportunityFormIdIn = new ArrayList<>();
            List<Long> paasFormIdIn = new ArrayList<>();
            for (PaasFormEntity formEntity : paasformList) {
                if (Objects.equals(formEntity.getSaasMark(), SaasMarkEnum.PAAS.getCode())) {
                    paasFormIdIn.add(formEntity.getId());
                }
                if (Objects.equals(formEntity.getBusinessType(), XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode())) {
                    customerFormList.add(formEntity);
                    customerFormIdIn.add(formEntity.getId());
                } else if (Objects.equals(formEntity.getBusinessType(), XbbRefTypeEnum.CONTRACT.getCode())) {
                    contractFormList.add(formEntity);
                } else if (Objects.equals(formEntity.getBusinessType(), XbbRefTypeEnum.SALES_OPPORTUNITY.getCode())) {
                    opportunityFormList.add(formEntity);
                    opportunityFormIdIn.add(formEntity.getId());
                } else if (Objects.equals(formEntity.getBusinessType(), XbbRefTypeEnum.CLUE.getCode())) {
                    clueFormList.add(formEntity);
                    clueFormIdIn.add(formEntity.getId());
                } else if (Objects.equals(formEntity.getBusinessType(), XbbRefTypeEnum.WORK_ORDER_V2.getCode())) {
                    workOrderV2FormList.add(formEntity);
                } else if (Objects.equals(formEntity.getBusinessType(), XbbRefTypeEnum.RECEIPT_ORDER.getCode())) {
                    receiptOrderFormList.add(formEntity);
                } else {
                    otherFormList.add(formEntity);
                }
            }

            List<PaasFormEntity> paasFormEntities = new ArrayList<>();
            // 记录要删除的系统表单id
            List<Long> formIdDels = new ArrayList<>();
            // 记录要新增的表单
            List<PaasFormEntity> formAddList = new ArrayList<>();
            if (!customerMirrorFormList.isEmpty()) {
                dealWithMoreForms(customerMirrorFormList, customerFormList, corpid, paasFormEntities, formIdDels, formAddList, creatorId);
            }
            if (!contractMirrorFormList.isEmpty()) {
                dealWithMoreForms(contractMirrorFormList, contractFormList, corpid, paasFormEntities, formIdDels, formAddList, creatorId);
            }
            if (!opportunityFormList.isEmpty()) {
                dealWithMoreForms(opportunityMirrorFormList, opportunityFormList, corpid, paasFormEntities, formIdDels, formAddList, creatorId);
            }
            if (!clueFormList.isEmpty()) {
                dealWithMoreForms(clueMirrorFormList, clueFormList, corpid, paasFormEntities, formIdDels, formAddList, creatorId);
            }
            if (!workOrderV2FormList.isEmpty()) {
                dealWithMoreForms(workOrderV2MirrorFormList,workOrderV2FormList,corpid,paasFormEntities,formIdDels,formAddList,creatorId);
            }
            if (!receiptOrderFormList.isEmpty()) {
                dealWithMoreForms(receiptOrderMirrorFormList,receiptOrderFormList,corpid,paasFormEntities,formIdDels,formAddList,creatorId);
            }
            if (!otherMirrorFormList.isEmpty()) {
                Map<Integer, Long> mirrorFormMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                Map<Long, PaasFormEntity> paasFormMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                otherMirrorFormList.forEach(item -> mirrorFormMap.put(item.getBusinessType(), item.getId()));
                otherFormList.forEach(item -> {
                    Long mirrorFormId = mirrorFormMap.get(item.getBusinessType());
                    paasFormMap.put(mirrorFormId, item);
                });
                for (MirrorFormEntity entity : otherMirrorFormList) {
                    PaasFormEntity paasFormEntity = paasFormMap.get(entity.getId());
                    setFormList(paasFormEntities, entity, paasFormEntity, now);
                }
            }
            if (!paasFormEntities.isEmpty()) {
                paasFormModel.updateBatch(paasFormEntities, corpid);
                installFormList.addAll(paasFormEntities);
            }
            if (formIdDels.size() > 0) {
                paasFormModel.deleteBatch(formIdDels, corpid);
            }
            if (!formAddList.isEmpty()) {
                // 多余的新增
                paasFormModel.insertBatch(formAddList);
                // 工单v2新增的表单需要绑定在默认目录上
                List<PaasFormEntity> addWorkOrderV2FormList = new ArrayList<>();
                //这边新增完表单之后，判断需不需要往tb_paas_form_ref插入对应的关系,插入的都是子表单，主要是为了BI关联产品统计使用
                Set<Integer> allBusinessTypes = BIProductReformEnum.getAllBusinessTypes();
                List<PaasFormEntity> shouldCompareMainPaasFormList = new ArrayList<>();
                formAddList.forEach(paasFormEntity -> {
                    if (allBusinessTypes.contains(paasFormEntity.getBusinessType())) {
                        shouldCompareMainPaasFormList.add(paasFormEntity);
                    }
                    if (Objects.equals(XbbRefTypeEnum.WORK_ORDER_V2.getCode(),paasFormEntity.getBusinessType())) {
                        addWorkOrderV2FormList.add(paasFormEntity);
                    }
                });
                if (CollectionsUtil.isNotEmpty(shouldCompareMainPaasFormList)) {
                    paasFormModel.insertSubProductFormList(shouldCompareMainPaasFormList, allBusinessTypes);
                }
                if (CollectionsUtil.isNotEmpty(addWorkOrderV2FormList)) {
                    // 工单v2新增的表单绑定在默认目录上
                    BaseIdListDTO baseIdListDTO = new BaseIdListDTO();
                    baseIdListDTO.setPlatform(PlatFormEnum.WEB.getValue());
                    baseIdListDTO.setCorpid(corpid);
                    baseIdListDTO.setUserId(creatorId);
                    List<Long> v2FormIds = addWorkOrderV2FormList.stream().map(PaasFormEntity::getId).collect(Collectors.toList());
                    baseIdListDTO.setIdList(v2FormIds);
                    workOrderV2FeignClient.bindToDefault(baseIdListDTO,LocaleContextHolder.getLocale().toString());
                    // 创建默认流程
                    UserVO userVO = new UserVO();
                    userVO.setCorpid(corpid);
                    userVO.setUserId(creatorId);
                    baseIdListDTO.setLoginUser(userVO);
                    workOrderV2FeignClient.createDefaultProcess(baseIdListDTO,LocaleContextHolder.getLocale().toString());
                }
                // 记录安装后的表单
                installFormList.addAll(formAddList);
            }

            List<Long> formIdIn = new ArrayList<>();
            paasformList.forEach(item -> formIdIn.add(item.getId()));

            // 备份业务规则
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("corpid", corpid);
            param.put("formIdIn", formIdIn);
            param.put("del", DelEnum.NORMAL.getDel());
            List<BusinessRuleInfoEntity> ruleList = businessRuleInfoModel.findEntitys(param);
            if (!ruleList.isEmpty()) {
                // 备份
                List<BusinessRuleInfoRetainEntity> ruleInfoRetainEntities = new ArrayList<>();
                ruleList.forEach(item -> {
                    BusinessRuleInfoRetainEntity entity = new BusinessRuleInfoRetainEntity(item.getSaasMark(), item.getBusinessType(), item.getCorpid(), retainAppId, item.getAppId(), item.getMenuId(), item.getFormId(), item.getId(),
                            item.getActiveName(), item.getActiveType(), item.getActiveSaasMark(), item.getActiveBusinessType(), item.getActiveFormId(), item.getActiveMenuId(), item.getActiveAppId(), item.getActiveOperationType(), item.getActivePreConditions(), item.getActiveFilterConditions(), item.getActiveOperations(), item.getActiveMemo(), item.getActiveEnable(),
                            item.getInactiveName(), item.getInactiveType(), item.getInactiveSaasMark(), item.getInactiveBusinessType(), item.getInactiveFormId(), item.getInactiveMenuId(), item.getInactiveAppId(), item.getInactiveOperationType(), item.getInactivePreConditions(), item.getInactiveFilterConditions(), item.getInactiveOperations(), item.getInactiveMemo(), item.getInactiveEnable(), item.getPriority(), creatorId, now, now, item.getDel());
                    ruleInfoRetainEntities.add(entity);
                });
                if (!ruleInfoRetainEntities.isEmpty()) {
                    businessRuleInfoRetainModel.insertBatch(ruleInfoRetainEntities);
                }
                // 删除规则
                List<Long> ruleIdIn = new ArrayList<>();
                ruleList.forEach(item -> ruleIdIn.add(item.getId()));
                businessRuleInfoModel.deleteBatch(ruleIdIn, corpid);
            }

            // 备份表单解释
            param.clear();
            param.put("corpid", corpid);
            param.put("formIdIn", formIdIn);
            param.put("del", DelEnum.NORMAL.getDel());
            List<PaasFormExplainEntity> explianList = paasFormExplainModel.list(param);
            if (!explianList.isEmpty()) {
                //备份解释
                List<PaasFormExplainRetainEntity> paasFormExplainRetainEntities = new ArrayList<>();
                explianList.forEach(item->{
                    PaasFormExplainRetainEntity paasFormExplainRetainEntity = new PaasFormExplainRetainEntity(item);
                    paasFormExplainRetainEntity.setRetainAppId(retainAppId);
                    paasFormExplainRetainEntities.add(paasFormExplainRetainEntity);
                });
                paasFormExplainRetainModel.insertBatch(paasFormExplainRetainEntities);
                //删除解释
                List<Long> formExplainIdIn = new ArrayList<>();
                explianList.forEach(item -> formExplainIdIn.add(item.getId()));
                paasFormExplainModel.deleteBatch(formExplainIdIn, corpid);
            }

            //备份阶段推进器 并删除相应原数据  注：好像没有Paas，先放着吧
            StageRetainPojo stageRetainPojo = new StageRetainPojo(corpid, retainAppId, customerFormIdIn, clueFormIdIn, opportunityFormIdIn, paasFormIdIn);
            retainStageThruster(stageRetainPojo);

            // 打印模板
            param.clear();
            param.put("corpid", corpid);
            param.put("formIdIn", formIdIn);
            param.put("del", DelEnum.NORMAL.getDel());
            List<PaasPrintTemplateEntity> printTemplateList = paasPrintTemplateDao.findEntitys(param);
            if (!printTemplateList.isEmpty()) {
                List<PaasPrintTemplateRetainEntity> paasPrintTemplateRetainEntities = new ArrayList<>();
                printTemplateList.forEach(item->{
                    PaasPrintTemplateRetainEntity paasPrintTemplateRetainEntity = new PaasPrintTemplateRetainEntity(item);
                    paasPrintTemplateRetainEntity.setRetainAppId(retainAppId);
                    paasPrintTemplateRetainEntities.add(paasPrintTemplateRetainEntity);
                });
                paasPrintTemplateRetainModel.insertBatch(paasPrintTemplateRetainEntities);

                // 删除打印模板
                List<Long> printTemplateIdIn = new ArrayList<>();
                printTemplateList.forEach(item -> printTemplateIdIn.add(item.getId()));
                paasPrintTemplateDao.deleteBatch(printTemplateIdIn, corpid);
            }
            // 备份标签分组
            param.clear();
            param.put("corpid", corpid);
            param.put("formIdIn", formIdIn);
            param.put("del", DelEnum.NORMAL.getDel());
            List<LabelGroupEntity> labelGroupEntities = labelGroupModel.findEntitys(param);
            if (!labelGroupEntities.isEmpty()){
                List<MirrorRetainLabelGroupEntity> mirrorRetainLabelGroupEntities = new ArrayList<>();
                List<Long> groupIds = new ArrayList<>();
                labelGroupEntities.forEach(item->{
                    MirrorRetainLabelGroupEntity mirrorRetainLabelGroupEntity = new MirrorRetainLabelGroupEntity();
                    mirrorRetainLabelGroupEntity.setRetainAppId(retainAppId);
                    mirrorRetainLabelGroupEntity.setCorpid(corpid);
                    mirrorRetainLabelGroupEntity.setSourceAppId(item.getAppId());
                    mirrorRetainLabelGroupEntity.setSourceFormId(item.getFormId());
                    mirrorRetainLabelGroupEntity.setSourceGroupId(item.getId());
                    mirrorRetainLabelGroupEntity.setGroupName(item.getGroupName());
                    mirrorRetainLabelGroupEntity.setBusinessType(item.getBusinessType());
                    mirrorRetainLabelGroupEntity.setSort(item.getSort());
                    mirrorRetainLabelGroupEntity.setCreatorId(item.getCreatorId());
                    mirrorRetainLabelGroupEntity.setAddTime(item.getAddTime());
                    mirrorRetainLabelGroupEntity.setUpdateTime(item.getUpdateTime());
                    mirrorRetainLabelGroupEntity.setDel(item.getDel());
                    mirrorRetainLabelGroupEntities.add(mirrorRetainLabelGroupEntity);
                    groupIds.add(item.getId());

                });
                mirrorRetainLabelGroupModel.insertBatch(mirrorRetainLabelGroupEntities);

                labelGroupModel.deletebatch(groupIds, corpid);

                // 备份标签
                param.clear();
                param.put("corpid", corpid);
                param.put("formIdIn", formIdIn);
                param.put("del", DelEnum.NORMAL.getDel());
                List<LabelEntity> labelEntities = labelModel.findEntitys(param);
                if (labelEntities.size()>0){
                    List<Long> labelIds = new ArrayList<>();
                    List<MirrorRetainLabelEntity> mirrorRetainLabelEntities = new ArrayList<>();
                    labelEntities.forEach(item->{
                        labelIds.add(item.getId());
                        MirrorRetainLabelEntity mirrorRetainLabelEntity = new MirrorRetainLabelEntity();
                        mirrorRetainLabelEntity.setRetainAppId(retainAppId);
                        mirrorRetainLabelEntity.setCorpid(corpid);
                        mirrorRetainLabelEntity.setSourceAppId(item.getAppId());
                        mirrorRetainLabelEntity.setSourceFormId(item.getFormId());
                        mirrorRetainLabelEntity.setSourceLabelId(item.getId());
                        mirrorRetainLabelEntity.setSourceGroupId(item.getGroupId());
                        mirrorRetainLabelEntity.setBusinessType(item.getBusinessType());
                        mirrorRetainLabelEntity.setName(item.getName());
                        mirrorRetainLabelEntity.setColor(item.getColor());
                        mirrorRetainLabelEntity.setSort(item.getSort());
                        mirrorRetainLabelEntity.setEnable(item.getEnable());
                        mirrorRetainLabelEntity.setCreatorId(item.getCreatorId());
                        mirrorRetainLabelEntity.setAddTime(item.getAddTime());
                        mirrorRetainLabelEntity.setUpdateTime(item.getUpdateTime());
                        mirrorRetainLabelEntity.setDel(item.getDel());
                        mirrorRetainLabelEntities.add(mirrorRetainLabelEntity);
                    });
                    mirrorRetainLabelModel.insertBatch(mirrorRetainLabelEntities);

                    labelModel.deleteBatch(labelIds,corpid);
                }
            }

            // 备份低代码
            lowCodeRetaion(corpid, retainAppId, new HashSet<>(formIdIn), XbbRefTypeEnum.SYSTEM.getCode());

        }
        return retainAppId;
    }

    /**
     * 备份阶段推进器的3张表(stageProcess、stage、stageWork) 注：不安装新数据
     * @param stageRetainPojo
     */
    public void retainStageThruster(StageRetainPojo stageRetainPojo) {
        if (stageRetainPojo.getStageFormIdIn().isEmpty()) {
            return;
        }

        retainStageProcess(stageRetainPojo);
        retainStage(stageRetainPojo);
        retainStageWork(stageRetainPojo);
    }

    private void retainStageProcess(StageRetainPojo stageRetainPojo) {
        // 备份 阶段流程表
        Map<String, Object> param = new HashMap<>();
        param.put("corpid", stageRetainPojo.getCorpid());
        param.put("formIdIn", stageRetainPojo.getStageFormIdIn());
        param.put("del", DelEnum.NORMAL.getDel());
        List<StageProcessEntity> stageProcessEntityList = stageProcessModel.findEntitys(param);
        List<Long> stageProcessIdIn = stageRetainPojo.getStageProcessIdIn();

        if (!stageProcessEntityList.isEmpty()) {
            List<StageProcessRetainEntity> stageProcessRetainEntityList = new ArrayList<>();

            stageProcessEntityList.forEach(item -> {
                StageProcessRetainEntity entity = new StageProcessRetainEntity(item, stageRetainPojo.getRetainAppId());
                stageProcessRetainEntityList.add(entity);
                stageProcessIdIn.add(item.getId());
            });
            stageProcessRetainModel.insertBatch(stageProcessRetainEntityList);
            // 删除原来的信息
            stageProcessModel.deleteBatch(stageProcessIdIn, stageRetainPojo.getCorpid());
        }
    }

    private void retainStage(StageRetainPojo stageRetainPojo) {
        // 备份 stage （客户、线索、机会、Paas）
        List<StageRetainEntity> allRetainStageEntity = new ArrayList<>();

        retainSingleStage(allRetainStageEntity, stageRetainPojo, stageRetainPojo.getPaasFormIdIn(), SaasMarkEnum.PAAS.getCode(), 0);
        retainSingleStage(allRetainStageEntity, stageRetainPojo, stageRetainPojo.getCustomerFormIdIn(), SaasMarkEnum.SAAS.getCode(), XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode());
        retainSingleStage(allRetainStageEntity, stageRetainPojo, stageRetainPojo.getClueFormIdIn(), SaasMarkEnum.SAAS.getCode(), XbbRefTypeEnum.CLUE.getCode());
        retainSingleStage(allRetainStageEntity, stageRetainPojo, stageRetainPojo.getOpportunityFormIdIn(), SaasMarkEnum.SAAS.getCode(), XbbRefTypeEnum.SALES_OPPORTUNITY.getCode());

        if (!allRetainStageEntity.isEmpty()) {
            stageRetainModel.insertBatch(allRetainStageEntity);
        }
    }

    private void retainSingleStage(List<StageRetainEntity> allRetainStageEntity, StageRetainPojo stageRetainPojo, List<Long> formIdIn, Integer saasMark, Integer businessType) {
        String corpid = stageRetainPojo.getCorpid();
        Long retainAppId = stageRetainPojo.getRetainAppId();
        List<Long> stageProcessIdIn = stageRetainPojo.getStageProcessIdIn();
        List<Long> stageIdIn = stageRetainPojo.getStageIdIn();
        if (formIdIn.isEmpty()) {
            return;
        }
        Map<String, Object> param = new HashMap<>();
        param.put("corpid", corpid);
        param.put("stageProcessIdIn", stageProcessIdIn);
//        param.put("formIdIn", formIdIn);
        param.put("del", DelEnum.NORMAL.getDel());
        StageHandle stageHandle = stageHandleFactory.getStageHandle(saasMark, businessType);
        List<PaasStageEntity> stageEntityList = stageHandle.findEntitys(param);

        if (!stageEntityList.isEmpty()) {
            //备份原数据
            List<StageRetainEntity> stageRetainEntityList = new ArrayList<>();
            stageEntityList.forEach(item -> {
                StageRetainEntity entity = new StageRetainEntity(item, saasMark, businessType, retainAppId);
                stageRetainEntityList.add(entity);
                stageIdIn.add(item.getId());
            });
            allRetainStageEntity.addAll(stageRetainEntityList);
            //删除原数据
            stageHandle.deleteBatch(stageEntityList, corpid);
        }
    }


    private void retainStageWork(StageRetainPojo stageRetainPojo) {
        String corpid = stageRetainPojo.getCorpid();
        List<Long> stageIdIn = stageRetainPojo.getStageIdIn();
        Long retainAppId = stageRetainPojo.getRetainAppId();
        if (stageIdIn.isEmpty()) {
            return;
        }
        // 备份 阶段任务 （客户、线索、机会）
        Map<String, Object> param = new HashMap<>();
        param.put("corpid", corpid);
        param.put("stageIdIn", stageIdIn);
//        param.put("formIdIn", stageRetainPojo.getStageFormIdIn());
        param.put("del", DelEnum.NORMAL.getDel());

        List<StageWorkEntity> stageWorkEntityList = stageWorkModel.findEntitys(param);

        if (!stageWorkEntityList.isEmpty()) {
            List<StageWorkRetainEntity> stageWorkRetainEntityList = new ArrayList<>();
            List<Long> stageWorkIdIn = new ArrayList<>();
            stageWorkEntityList.forEach(item -> {
                StageWorkRetainEntity entity = new StageWorkRetainEntity(item, retainAppId);
                stageWorkRetainEntityList.add(entity);
                stageWorkIdIn.add(item.getId());
            });
            stageWorkRetainModel.insertBatch(stageWorkRetainEntityList);
            // 删除原来的信息
            stageWorkModel.deleteBatch(stageWorkIdIn);
        }
    }
    /**
     * 备份低代码
     * @param corpid
     * @param formIdIn
     * @param businessType
     * @throws XbbException
     */
    private void lowCodeRetaion(String corpid, Long retainAppId, Set<Long> formIdIn, Integer businessType) throws XbbException {

        List<LowCodeJsFileEntity> lowCodeJsFileEntityList = getJsFileList(corpid, formIdIn, businessType);
        if (CollectionsUtil.isEmpty(lowCodeJsFileEntityList)) {
            return;
        }

        List<CustomButtonPojo> customButtonPojoList = getButtonList(corpid, formIdIn, businessType);
        Map<Long, List<LowCodeActionSettingPojo>> actionSettingListMap= getActionSettingList(corpid, formIdIn, businessType);
        Map<Long, List<CustomButtonPojo>> customButtonPojoMap = new HashMap<>();
        if (CollectionsUtil.isNotEmpty(customButtonPojoList)) {
            customButtonPojoMap = customButtonPojoList.stream().collect(Collectors.groupingBy(CustomButtonPojo::getFormId));
        }
        List<LowCodeRetaionEntity> lowCodeRetaionEntityList = new ArrayList<>(lowCodeJsFileEntityList.size());
        for (LowCodeJsFileEntity lowCodeJsFileEntity : lowCodeJsFileEntityList) {
            LowCodeRetaionEntity lowCodeRetaionEntity = new LowCodeRetaionEntity();
            lowCodeRetaionEntity.setSourceFrom(BasicConstant.TWO);

            lowCodeRetaionEntity.setCorpid(corpid);
            lowCodeRetaionEntity.setRetainAppId(retainAppId);
            lowCodeRetaionEntity.setSourceFormId(lowCodeJsFileEntity.getFormId());
            lowCodeRetaionEntity.setSourceId(lowCodeJsFileEntity.getId());
            lowCodeRetaionEntity.setSourceSaasMark(lowCodeJsFileEntity.getSaasMark());
            lowCodeRetaionEntity.setSourceBusinessType(lowCodeJsFileEntity.getBusinessType());

            lowCodeRetaionEntity.setWhetherToPublish(lowCodeJsFileEntity.getWhetherToPublish());
            lowCodeRetaionEntity.setCreatorId(lowCodeJsFileEntity.getCreatorId());
            long now = DateTimeUtil.getInt();
            lowCodeRetaionEntity.setAddTime(now);
            lowCodeRetaionEntity.setUpdateTime(now);
            lowCodeRetaionEntity.setDel(DelEnum.NORMAL.getDel());

            //备份低代码配置
            LowCodeConfigRetaionPojo lowCodeConfigRetaionPojo = new LowCodeConfigRetaionPojo();
            List<CustomButtonPojo> customButtonPojos = customButtonPojoMap.get(lowCodeJsFileEntity.getFormId());
            List<LowCodeActionSettingPojo> lowCodeActionSettingPojos = actionSettingListMap.get(lowCodeJsFileEntity.getFormId());
            lowCodeConfigRetaionPojo.setCustomButtonPojoList(customButtonPojos);
            lowCodeConfigRetaionPojo.setActionSettingList(lowCodeActionSettingPojos);
            lowCodeRetaionEntity.setLowCodeConfig(JSONObject.toJSONString(lowCodeConfigRetaionPojo));

            //拷贝低代码文件
            if (Objects.isNull(lowCodeJsFileEntity.getPublish()) || StringUtil.isEmpty(lowCodeJsFileEntity.getPublish()) || Objects.equals(lowCodeJsFileEntity.getPublish(), "null")) {
                // 没有发布版本，默认取未发布进行备份
                String lowCodeBackFile = copyLowCodeBackFile(lowCodeJsFileEntity.getUnreleased());
                lowCodeRetaionEntity.setPublish(lowCodeBackFile);
            } else {
                String lowCodeBackFile = copyLowCodeBackFile(lowCodeJsFileEntity.getPublish());
                lowCodeRetaionEntity.setPublish(lowCodeBackFile);
            }

            lowCodeRetaionEntityList.add(lowCodeRetaionEntity);
        }
        if (CollectionsUtil.isNotEmpty(lowCodeRetaionEntityList)) {
            lowCodeRetaionModel.insertBatch(lowCodeRetaionEntityList);
        }
    }

    /**
     * 拷贝低代码文件
     * @param jsFilePojoStr
     * @return
     */
    public String copyLowCodeBackFile(String jsFilePojoStr) {
        try {
            if (Objects.nonNull(jsFilePojoStr) && StringUtil.isNotEmpty(jsFilePojoStr)) {
                JsFilePojo jsFilePojo = JSON.parseObject(jsFilePojoStr, JsFilePojo.class);
                if (Objects.isNull(jsFilePojo) || Objects.isNull(jsFilePojo.getAttachIndex())) {
                    return null;
                }
                String host = "https://" + proBaseConfig.getLowCodeBucketName() + "." + proBaseConfig.getLowCodeEndpointSimple();
                String sourceFileName = jsFilePojo.getFilename();
                String attachIndex = jsFilePojo.getAttachIndex();
                String fileDir = attachIndex.replace(host, "");
                String route = fileDir.replace(sourceFileName, "");
                route = route.substring(1);
                String[] sourceFile = sourceFileName.split(StringConstant.CROSS);
                String templateFieName = sourceFile[0] + StringConstant.CROSS + StringConstant.LOW_CODE_BACK_SUFFIX;
                String templateFieDir = route + templateFieName;
                String sourceFieDir = route + sourceFileName;
                aliyunOssService.copyLowCodeFile(sourceFieDir, templateFieDir);
                String templateIndex = host + "/" + templateFieDir;
                jsFilePojo.setAttachIndex(templateIndex);
                jsFilePojo.setFilename(templateFieName);
                return JSON.toJSONString(jsFilePojo);
            }
        } catch (Exception e) {
            LOG.error("保存低代码镜像失败! ", e);
        }
        return null;
    }

    /**
     * 获取自定义按钮
     * @param corpid
     * @param formIdIn
     * @param businessType
     * @return
     * @throws XbbException
     */
    public List<CustomButtonPojo> getButtonList(String corpid, Set<Long> formIdIn, Integer businessType) throws XbbException {
        List<CustomButtonPojo> customButtonList = new ArrayList<>();
        try {
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put(StringConstant.CORPID, corpid);
            param.put(StringConstant.DEL, DelEnum.NORMAL.getDel());
            param.put("formIdIn", formIdIn);
            if (Objects.equals(businessType, XbbRefTypeEnum.WORK_ORDER.getCode())) {
                param.put(StringConstant.BUSINESS_TYPE, XbbRefTypeEnum.WORK_ORDER.getCode());
            }
            List<CustomButtonEntity> buttonList = customButtonModel.findEntitys(param);
            if (CollectionsUtil.isEmpty(buttonList)) {
                return new ArrayList<>();
            }

            List<Long> buttonIdList = buttonList.stream().map(CustomButtonEntity::getId).collect(Collectors.toList());
            Map<Long, List<CustomButtonActionPojo>> actionMap = new HashMap<>(buttonList.size());

            param.put("buttonIdIn", buttonIdList);
            List<CustomButtonActionEntity> buttonActionList = customButtonActionModel.findEntitys(param);
            buttonActionList.forEach(item -> {
                if (actionMap.containsKey(item.getButtonId())) {
                    List<CustomButtonActionPojo> customButtonActionList = actionMap.get(item.getButtonId());
                    CustomButtonActionPojo customButtonActionPojo = new CustomButtonActionPojo(item.getId(), item.getButtonId(), item.getActionType(), JSON.parseObject(item.getParam()), item.getSort());
                    customButtonActionList.add(customButtonActionPojo);
                    actionMap.put(item.getButtonId(), customButtonActionList);
                } else {
                    List<CustomButtonActionPojo> customButtonActionList = new ArrayList<>();
                    CustomButtonActionPojo customButtonActionPojo = new CustomButtonActionPojo(item.getId(), item.getButtonId(), item.getActionType(), JSON.parseObject(item.getParam()), item.getSort());
                    customButtonActionList.add(customButtonActionPojo);
                    actionMap.put(item.getButtonId(), customButtonActionList);
                }
            });
            buttonList.forEach(item -> {
                CustomButtonPojo customButtonPojo = new CustomButtonPojo(item.getId(), item.getName(), item.getIcon(), item.getColor(), item.getType(), item.getSort(), actionMap.get(item.getId()));
                customButtonPojo.setFormId(item.getFormId());
                customButtonList.add(customButtonPojo);
            });
            return customButtonList;
        } catch (Exception e) {
            LOG.error("MirrorServiceImpl.getButtonList error：", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
        }
    }

    /**
     * 获取低代码动作设置
     * @param corpid
     * @param formIdIn
     * @param businessType
     * @return
     * @throws XbbException
     */
    public Map<Long, List<LowCodeActionSettingPojo>> getActionSettingList(String corpid, Set<Long> formIdIn, Integer businessType) throws XbbException {
        Map<Long, List<LowCodeActionSettingPojo>> lowCodeActionSettingPojoMap = new HashMap<>();
        try {
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put(StringConstant.CORPID, corpid);
            param.put("formIdIn", formIdIn);
            param.put(StringConstant.DEL, DelEnum.NORMAL.getDel());
            if (Objects.equals(businessType, XbbRefTypeEnum.WORK_ORDER.getCode())) {
                param.put(StringConstant.BUSINESS_TYPE, XbbRefTypeEnum.WORK_ORDER.getCode());
            }
            param.put(ParameterConstant.ORDER_BY_STR, "sort ASC");
            List<LowCodeActionSettingEntity> lowCodeActionSettingList = lowCodeActionSettingModel.findEntitys(param);
            if (CollectionsUtil.isEmpty(lowCodeActionSettingList)) {
                return lowCodeActionSettingPojoMap;
            }
            for (LowCodeActionSettingEntity lowCodeActionSettingEntity : lowCodeActionSettingList) {
                List<LowCodeActionSettingPojo> lowCodeActionSettingPojos = lowCodeActionSettingPojoMap.get(lowCodeActionSettingEntity.getFormId());
                if (Objects.isNull(lowCodeActionSettingPojos)) {
                    lowCodeActionSettingPojos = new ArrayList<>();
                }
                LowCodeActionSettingPojo mirrorLowCodeActionSettingPojo = new LowCodeActionSettingPojo(lowCodeActionSettingEntity.getId(),
                        lowCodeActionSettingEntity.getType(), lowCodeActionSettingEntity.getTriggerTiming(), lowCodeActionSettingEntity.getActionType(),
                        lowCodeActionSettingEntity.getAttr(), lowCodeActionSettingEntity.getFieldType(),
                        JSON.parseObject(lowCodeActionSettingEntity.getParam()), lowCodeActionSettingEntity.getSort());
                lowCodeActionSettingPojos.add(mirrorLowCodeActionSettingPojo);
                lowCodeActionSettingPojoMap.put(lowCodeActionSettingEntity.getFormId(), lowCodeActionSettingPojos);
            }
            return lowCodeActionSettingPojoMap;
        }  catch (Exception e) {
            LOG.error("MirrorServiceImpl.getActionSettingList 出错", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
        }
    }

    /**
     * 获取低代码文件
     * @param corpid
     * @param formIdIn
     * @param businessType
     * @return
     * @throws XbbException
     */
    public List<LowCodeJsFileEntity> getJsFileList(String corpid, Set<Long> formIdIn, Integer businessType) throws XbbException {
        try {
            // 校验权限
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put(StringConstant.CORPID, corpid);
            param.put("formIdIn", formIdIn);
            param.put(StringConstant.DEL, DelEnum.NORMAL.getDel());
            if (Objects.equals(businessType, XbbRefTypeEnum.WORK_ORDER.getCode())) {
                param.put(StringConstant.BUSINESS_TYPE, XbbRefTypeEnum.WORK_ORDER.getCode());
            }
            return lowCodeJsFileModel.findEntitys(param);
        } catch (Exception e) {
            LOG.error("CloudCodeServiceImpl.getJsFileList 出错", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
        }
    }

    /**
     * 处理系统表单
     * @param mirrorFormList
     * @param customerFormList
     * @param corpid
     * @param paasFormEntities
     * @param formIdDels
     * @param formAddList
     * @param creatorId
     * @author xingxing.xiao
     */
    private void dealWithMoreForms (List<MirrorFormEntity> mirrorFormList, List<PaasFormEntity> customerFormList, String corpid, List<PaasFormEntity> paasFormEntities, List<Long> formIdDels, List<PaasFormEntity> formAddList, String creatorId) {
        long now = DateUtil.getInt();
        int mirrorCount = mirrorFormList.size();
        int count = customerFormList.size();
        if (mirrorCount == count) {
            // NO.1. 当镜像表单等于系统多表单数量时，覆盖全部
            for (int i = 0; i < mirrorFormList.size(); i++) {
                // 一一对应好了
                MirrorFormEntity entity = mirrorFormList.get(i);
                PaasFormEntity paasFormEntity = customerFormList.get(i);
                setFormList(paasFormEntities, entity, paasFormEntity, now);
            }
        } else if (mirrorCount < count) {
            // NO.2. 当镜像表单小于系统多表单数量时，覆盖部分，多余的删除
            for (int i = 0; i < customerFormList.size(); i++) {
                PaasFormEntity paasFormEntity = customerFormList.get(i);
                if (i < mirrorCount) {
                    MirrorFormEntity entity = mirrorFormList.get(i);
                    setFormList(paasFormEntities, entity, paasFormEntity, now);
                } else {
                    formIdDels.add(paasFormEntity.getId());
                }
            }
        } else {
            // NO.3. 当镜像表单大于系统多表单数量时，覆盖全部，多余的新增
            for (int i = 0; i < mirrorFormList.size(); i++) {
                MirrorFormEntity entity = mirrorFormList.get(i);
                if (i < count) {
                    PaasFormEntity paasFormEntity = customerFormList.get(i);
                    setFormList(paasFormEntities, entity, paasFormEntity, now);
                } else {
                    String associationList = new JSONArray().toJSONString();
                    String businessRules = new JSONArray().toJSONString();
                    String permission = new JSONArray().toJSONString();
                    // appid 和 menuId 任取一个用
                    Long appId = customerFormList.get(0).getAppId();
                    Long menuId = customerFormList.get(0).getMenuId();
                    PaasFormEntity formEntity = new PaasFormEntity(corpid, appId, menuId, entity.getName(), entity.getSaasMark(), entity.getBusinessType(), entity.getEnable(), 0, entity.getVerifyRule(), entity.getDisableFieldAssign(), entity.getFrontCache(), entity.getTitleType(), entity.getCustomTitle(), associationList, businessRules, entity.getSurcharge(), entity.getOpenDistribution(), entity.getPriceAssociation(), entity.getSummary(), entity.getLabels(), entity.getCoOpPerms(), permission, creatorId, now, now, entity.getDel(), entity.getFieldPosition(), entity.getHidden(), entity.getDistributorMark());
                    formEntity.setMirrorFormId(entity.getId());
                    formEntity.setMirrorAppId(entity.getMirrorAppId());
                    formEntity.setMirrorMenuId(entity.getMirrorMenuId());
                    formEntity.setSort(0);
                    formAddList.add(formEntity);
                }
            }
        }
    }

    /**
     * 处理系统表单
     * @param paasFormEntities
     * @param mirrorFormEntity
     * @param paasFormEntity
     * @param now
     * @author xingxing.xiao
     */
    private void setFormList(List<PaasFormEntity> paasFormEntities, MirrorFormEntity mirrorFormEntity, PaasFormEntity paasFormEntity, Long now) {
        paasFormEntity.setName(mirrorFormEntity.getName());
        paasFormEntity.setEnable(mirrorFormEntity.getEnable());
        paasFormEntity.setVerifyRule(mirrorFormEntity.getVerifyRule());
        paasFormEntity.setDisableFieldAssign(mirrorFormEntity.getDisableFieldAssign());
        paasFormEntity.setFrontCache(mirrorFormEntity.getFrontCache());
        paasFormEntity.setTitleType(mirrorFormEntity.getTitleType());
        paasFormEntity.setCustomTitle(mirrorFormEntity.getCustomTitle());
        // 处理关联列表(不复制，为 [])
        paasFormEntity.setAssociationList(new JSONArray().toJSONString());
        // 业务规则默认给"0"， 后面如果要复制，会单独处理的
        paasFormEntity.setBusinessRules("0");
        paasFormEntity.setSurcharge(mirrorFormEntity.getSurcharge());
        paasFormEntity.setOpenDistribution(mirrorFormEntity.getOpenDistribution());
        paasFormEntity.setPriceAssociation(mirrorFormEntity.getPriceAssociation());
        paasFormEntity.setSummary(mirrorFormEntity.getSummary());
        paasFormEntity.setLabels(mirrorFormEntity.getLabels());
        paasFormEntity.setCoOpPerms(mirrorFormEntity.getCoOpPerms());
        paasFormEntity.setPermission(new JSONArray().toJSONString());
        paasFormEntity.setUpdateTime(now);
        paasFormEntity.setMirrorFormId(mirrorFormEntity.getId());
        paasFormEntity.setMirrorMenuId(mirrorFormEntity.getMirrorMenuId());
        paasFormEntity.setMirrorAppId(mirrorFormEntity.getMirrorAppId());
        paasFormEntity.setFieldPosition(mirrorFormEntity.getFieldPosition());
        paasFormEntities.add(paasFormEntity);
    }

    /**
     * 获取应用最大排序
     * @param corpid
     * @return
     */
    public Integer getAppMaxSort(String corpid) throws XbbException {
        Integer sort;
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("columns", "id, sort");
        param.put("corpid", corpid);
        param.put("orderByStr", "sort desc");
        param.put("del", DelEnum.NORMAL.getDel());
        List<PaasAppEntity> appList = paasAppModel.findEntitys(param);
        if(Objects.isNull(appList)){
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.APP_NOT_EXIST);
        }
        PaasAppEntity paasAppEntity = appList.get(0);
        Integer count = appList.size();
        if (paasAppEntity.getSort() > count) {
            sort = paasAppEntity.getSort();
        } else {
            sort = count;
        }
        return sort;
    }

    /**
     * 获取菜单最大排序
     * @param corpid
     * @return
     */
    public Integer getMenuMaxSort(String corpid, Long appId) {
        Map<String, Object> params = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        params.put("corpid", corpid);
        params.put("appId", appId);
        params.put("typeIn", Arrays.asList(MenuTypeEnum.FORM.getType(), MenuTypeEnum.PROCESS_FORM.getType()));
        params.put("del", DelEnum.NORMAL.getDel());
        return paasMenuModel.getEntitysCount(params);
    }
    /**
     * 获取工单分类最大排序
     * @return
     * @author xingxing.xiao
     */
    public Integer getCategoryMaxSort(String corpid) {
        Map<String, Object> params = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        params.put("corpid", corpid);
        params.put("del", DelEnum.NORMAL.getDel());
        return workOrderTemplateCategoryModel.getEntitysCount(params);
    }

    /**
     * 处理字段解释
     * @param explainList
     * @param appId
     * @param paasMenuMap
     * @param paasFormMap
     * @param corpid
     * @param sourceToNewFormIdMap
     * @param businessTypeToNewFormMap
     * @param workOrderDefaultToNewFormMap
     * @param isWorkOrder 是否工单，工单菜单要特殊处理
     * @return
     * @author xingxing.xiao
     */
    private String dealWithExplains(List<FieldAttrEntity> explainList, Long appId, Map<Long, Long> paasMenuMap, Map<Long, Long> paasFormMap, String corpid, Map<Long, Long> sourceMenuMirrorMap, Map<Long, Long> sourceFormMirrorMap, HashMap<Long, PaasFormEntity> sourceToNewFormIdMap, HashMap<Integer, PaasFormEntity> businessTypeToNewFormMap, HashMap<Integer, WorkOrderFormEntity> workOrderDefaultToNewFormMap, boolean isWorkOrder) throws XbbException {
        for (Iterator<FieldAttrEntity> iterator = explainList.iterator(); iterator.hasNext(); ) {
            FieldAttrEntity item = iterator.next();
            dealWithFieldEntity(item, appId, paasMenuMap, paasFormMap, corpid, sourceMenuMirrorMap, sourceFormMirrorMap, sourceToNewFormIdMap, businessTypeToNewFormMap, workOrderDefaultToNewFormMap, isWorkOrder);
            // 子表单
            if (Objects.nonNull(item.getSubForm())) {
                List<? extends FieldAttrEntity> subExplainList = item.getSubForm().getItems();
                // 加上判空，例： 退货退款表单里的退货产品字段
                if (Objects.nonNull(subExplainList)) {
                    for (FieldAttrEntity fieldAttrEntity : subExplainList) {
                        dealWithFieldEntity(fieldAttrEntity, appId, paasMenuMap, paasFormMap, corpid, sourceMenuMirrorMap, sourceFormMirrorMap, sourceToNewFormIdMap, businessTypeToNewFormMap, workOrderDefaultToNewFormMap, isWorkOrder);
                    }
                    item.getSubForm().setItems(subExplainList);
                }
            }
        }
        return JSONObject.toJSONString(explainList);
    }

    /**
     * 处理字段解释
     * @param item
     * @param appId
     * @param paasMenuMap
     * @param paasFormMap
     * @param corpid
     * @param sourceMenuMirrorMap
     * @param sourceFormMirrorMap
     * @param sourceToNewFormIdMap
     * @param businessTypeToNewFormMap
     * @param workOrderDefaultToNewFormMap
     * @param isWorkOrder
     * @author xingxing.xiao
     */
    private void dealWithFieldEntity (FieldAttrEntity item, Long appId, Map<Long, Long> paasMenuMap, Map<Long, Long> paasFormMap, String corpid, Map<Long, Long> sourceMenuMirrorMap, Map<Long, Long> sourceFormMirrorMap, HashMap<Long, PaasFormEntity> sourceToNewFormIdMap, HashMap<Integer, PaasFormEntity> businessTypeToNewFormMap, HashMap<Integer, WorkOrderFormEntity> workOrderDefaultToNewFormMap, boolean isWorkOrder) throws XbbException {

        // 可见可编辑
        item.setVisibleScopeEnable(0);
        VisibleRulePoJo visibleRulePoJo = new VisibleRulePoJo();
        visibleRulePoJo.setType(2);
        item.setVisibleScopeRule(visibleRulePoJo);
        item.setEditableAdvanceEnable(0);
        VisibleRulePoJo editableRulePoJo = new VisibleRulePoJo();
        editableRulePoJo.setType(2);
        item.setEditableRule(editableRulePoJo);
        // 可新增，可删除，例：添加负责人字段
        if (Objects.equals(item.getIsRedundant(), 1) && Objects.equals(item.getAttr(), FieldTypeEnum.OWNERID.getAlias())) {
            item.setAddAdvanceEnable(0);
            VisibleRulePoJo addRulePoJo = new VisibleRulePoJo();
            addRulePoJo.setType(2);
            item.setAddRule(addRulePoJo);
            item.setRemoveAdvanceEnable(0);
            VisibleRulePoJo removeRulePoJo = new VisibleRulePoJo();
            removeRulePoJo.setType(2);
            item.setRemoveRule(removeRulePoJo);
        }
        // 所属部门
        item.setRelatedMember("");

        // 数据联动 或者 下拉框-数据联动
        boolean dataRelyFlag = Objects.nonNull(item.getLinkForm()) && Objects.equals(item.getComboType(), ComboTypeEnum.DATARELY.getType());
        if (Objects.nonNull(item.getDefaultAttr()) && Objects.equals(item.getDefaultAttr().getDefaultType(), ComboTypeEnum.DATARELY.getAlias()) || dataRelyFlag) {
            RelyPoJo relyPoJo = item.getDefaultAttr().getRely();
            Integer businessType = relyPoJo.getSourceBusinessType();
            if (sourceFormMirrorMap.containsKey(relyPoJo.getSourceFormId())) {
                // 同一个镜像里的表单，可直接替换 （对应关系 sourceFormId -> mirrorFormId -> newFormId）
                relyPoJo.setSourceAppId(appId);
                if (isWorkOrder) {
                    relyPoJo.setSourceMenuId(paasMenuMap.get(0L));
                } else {
                    Long mirrorMenuId = sourceMenuMirrorMap.get(relyPoJo.getSourceMenuId());
                    relyPoJo.setSourceMenuId(paasMenuMap.get(mirrorMenuId));
                }
                Long mirrorFormId = sourceFormMirrorMap.get(relyPoJo.getSourceFormId());
                relyPoJo.setSourceFormId(paasFormMap.get(mirrorFormId));
            } else {
                // saas取默认表单(用businessType反查)，paas则从安装的镜像里匹配
                if (Objects.equals(relyPoJo.getSourceSaasMark(), SaasMarkEnum.SAAS.getCode())) {
                    // 区分工单和其他应用
                    if (Objects.equals(businessType, XbbRefTypeEnum.WORK_ORDER.getCode())) {
                        WorkOrderFormEntity workOrderFormEntity = workOrderDefaultToNewFormMap.get(businessType);
                        if (Objects.nonNull(workOrderFormEntity)) {
                            relyPoJo.setSourceAppId(workOrderFormEntity.getAppId());
                            relyPoJo.setSourceMenuId(workOrderFormEntity.getMenuId());
                            relyPoJo.setSourceFormId(workOrderFormEntity.getId());
                        }
                    } else {
                        // 客户、合同和单模板处理
                        PaasFormEntity paasFormEntity = businessTypeToNewFormMap.get(businessType);
                        if (Objects.nonNull(paasFormEntity)) {
                            relyPoJo.setSourceAppId(paasFormEntity.getAppId());
                            relyPoJo.setSourceMenuId(paasFormEntity.getMenuId());
                            relyPoJo.setSourceFormId(paasFormEntity.getId());
                        }
                    }
                } else {
                    PaasFormEntity formEntity = sourceToNewFormIdMap.get(relyPoJo.getSourceFormId());
                    if (Objects.nonNull(formEntity)) {
                        relyPoJo.setSourceAppId(formEntity.getAppId());
                        relyPoJo.setSourceMenuId(formEntity.getMenuId());
                        relyPoJo.setSourceFormId(formEntity.getId());
                    }
                }
            }
        }

        // 关联数据
        if (Objects.nonNull(item.getLinkInfo())) {
            LinkInfoPojo linkInfo = item.getLinkInfo();
            Integer businessType = linkInfo.getLinkBusinessType();
            if (sourceFormMirrorMap.containsKey(linkInfo.getLinkFormId())) {
                linkInfo.setLinkAppId(appId);
                if (isWorkOrder) {
                    linkInfo.setLinkMenuId(paasMenuMap.get(0L));
                } else {
                    Long mirrorMenuId = sourceMenuMirrorMap.get(linkInfo.getLinkMenuId());
                    linkInfo.setLinkMenuId(paasMenuMap.get(mirrorMenuId));
                }
                Long mirrorFormId = sourceFormMirrorMap.get(linkInfo.getLinkFormId());
                linkInfo.setLinkFormId(paasFormMap.get(mirrorFormId));
            } else {
                if (Objects.equals(linkInfo.getLinkSaasMark(), SaasMarkEnum.SAAS.getCode())) {
                    // 区分工单和其他应用
                    if (Objects.equals(businessType, XbbRefTypeEnum.WORK_ORDER.getCode())) {
                        WorkOrderFormEntity workOrderFormEntity = workOrderDefaultToNewFormMap.get(businessType);
                        if (Objects.nonNull(workOrderFormEntity)) {
                            linkInfo.setLinkAppId(workOrderFormEntity.getAppId());
                            linkInfo.setLinkMenuId(workOrderFormEntity.getMenuId());
                            linkInfo.setLinkFormId(workOrderFormEntity.getId());
                        }
                    } else {
                        // 单模板用 businessType 反查
                        PaasFormEntity paasFormEntity = businessTypeToNewFormMap.get(businessType);
                        if (Objects.nonNull(paasFormEntity)) {
                            linkInfo.setLinkAppId(paasFormEntity.getAppId());
                            linkInfo.setLinkMenuId(paasFormEntity.getMenuId());
                            linkInfo.setLinkFormId(paasFormEntity.getId());
                        }
                    }
                } else {
                    PaasFormEntity formEntity = sourceToNewFormIdMap.get(linkInfo.getLinkFormId());
                    if (Objects.nonNull(formEntity)) {
                        linkInfo.setLinkAppId(formEntity.getAppId());
                        linkInfo.setLinkMenuId(formEntity.getMenuId());
                        linkInfo.setLinkFormId(formEntity.getId());
                    }
                }
            }
        }

        // 下拉框 - 关联其他表单
        if (Objects.nonNull(item.getLinkForm())) {
            if (Objects.equals(item.getComboType(), ComboTypeEnum.LINKFORM.getType())) {
                LinkFormPoJo linkForm = item.getLinkForm();
                Integer businessType = linkForm.getSourceBusinessType();
                if (sourceFormMirrorMap.containsKey(linkForm.getSourceFormId())) {
                    linkForm.setSourceAppId(appId);
                    if (isWorkOrder) {
                        linkForm.setSourceMenuId(paasMenuMap.get(0L));
                    } else {
                        Long mirrorMenuId = sourceMenuMirrorMap.get(linkForm.getSourceMenuId());
                        linkForm.setSourceMenuId(paasMenuMap.get(mirrorMenuId));
                    }
                    Long mirrorFormId = sourceFormMirrorMap.get(linkForm.getSourceFormId());
                    linkForm.setSourceFormId(paasFormMap.get(mirrorFormId));
                } else {
                    if (Objects.equals(linkForm.getSourceSaasMark(), SaasMarkEnum.SAAS.getCode())) {
                        // 区分工单和其他应用
                        if (Objects.equals(businessType, XbbRefTypeEnum.WORK_ORDER.getCode())) {
                            WorkOrderFormEntity workOrderFormEntity = workOrderDefaultToNewFormMap.get(businessType);
                            if (Objects.nonNull(workOrderFormEntity)) {
                                linkForm.setSourceAppId(workOrderFormEntity.getAppId());
                                linkForm.setSourceMenuId(workOrderFormEntity.getMenuId());
                                linkForm.setSourceFormId(workOrderFormEntity.getId());
                            }
                        } else {
                            // 单模板用 businessType 反查
                            PaasFormEntity paasFormEntity = businessTypeToNewFormMap.get(businessType);
                            if (Objects.nonNull(paasFormEntity)) {
                                linkForm.setSourceAppId(paasFormEntity.getAppId());
                                linkForm.setSourceMenuId(paasFormEntity.getMenuId());
                                linkForm.setSourceFormId(paasFormEntity.getId());
                            }
                        }
                    } else {
                        PaasFormEntity formEntity = sourceToNewFormIdMap.get(linkForm.getSourceFormId());
                        if (Objects.nonNull(formEntity)) {
                            linkForm.setSourceAppId(formEntity.getAppId());
                            linkForm.setSourceMenuId(formEntity.getMenuId());
                            linkForm.setSourceFormId(formEntity.getId());
                        }
                    }
                }
            }
        }
    }

    /**
     * 查询工单默认表单
     * @param corpid
     * @author xingxing.xiao
     */
    private WorkOrderFormEntity getWorkOrderDefaultForm(String corpid) throws XbbException {
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        // 查询工单默认模板
        param.put("columns", "id, name, is_default, enable");
        param.put("corpid", corpid);
        param.put("isDefault", 1);
        param.put("del", DelEnum.NORMAL.getDel());
        List<WorkOrderTemplateEntity> templateEntityList = workOrderTemplateModel.findEntity(param);
        if (CollectionUtils.isEmpty(templateEntityList)) {
            // 工单默认模板 不能删除，如果没查到表单说明工单应用没开启
            throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403009, MirrorErrorCodeEnum.API_ERROR_403009.getMsg(), SystemAppMenuEnum.WORK_ORDER.getName());
        }

        // 查询工单默认模板下的默认表单
        param.clear();
        param.put("columns", "id, app_id, menu_id");
        param.put("corpid", corpid);
        param.put("templateId", templateEntityList.get(0).getId());
        param.put("enable", 1);
        param.put("del", DelEnum.NORMAL.getDel());
        param.put("orderByStr", "id ASC");
        List<WorkOrderFormEntity> formList = workOrderFormModel.findEntitys(param);
        if (CollectionUtils.isEmpty(formList)) {
            throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403009, MirrorErrorCodeEnum.API_ERROR_403009.getMsg(), SystemAppMenuEnum.WORK_ORDER.getName());
        }
        return formList.get(0);
    }

    /**
     * 封装菜单
     * @param entity
     * @param menuList
     * @param corpid
     * @param appId
     * @param creatorId
     * @param now
     * @author xingxing.xiao
     */
    private void setMenuList(MirrorMenuEntity entity, List<PaasMenuEntity> menuList, String corpid, Long appId, String creatorId, Long now) {
        PaasMenuEntity menuEntity = new PaasMenuEntity();
        menuEntity.setCorpid(corpid);
        menuEntity.setAppId(appId);
        menuEntity.setParentId(entity.getParentId());
        menuEntity.setName(entity.getName());
        menuEntity.setIcon(entity.getIcon());
        menuEntity.setColor(entity.getColor());
        menuEntity.setSort(entity.getSort());
        menuEntity.setType(entity.getType());
        menuEntity.setAlias(entity.getAlias());
        menuEntity.setSaasMark(entity.getSaasMark());
        menuEntity.setEnable(entity.getEnable());
        menuEntity.setUrl(entity.getUrl());
        menuEntity.setMobileUrl(entity.getMobileUrl());
        menuEntity.setCreatorId(creatorId);
        menuEntity.setAddTime(now);
        menuEntity.setUpdateTime(now);
        menuEntity.setDel(entity.getDel());
        menuEntity.setMirrorMenuId(entity.getId());
        menuEntity.setDistributorMark(entity.getDistributorMark());
        menuList.add(menuEntity);
    }

    /**
     * 镜像菜单列表
     * @param mirrorMenuListDTO
     * @return
     * @throws XbbException
     * @author xingxing.xiao
     */
    @Override
    public MirrorMenuListVO getMenuList(MirrorMenuListDTO mirrorMenuListDTO) throws XbbException {
        MirrorMenuListVO mirrorMenuListVO = new MirrorMenuListVO();
        try {
            Long mirrorId = mirrorMenuListDTO.getMirrorId();
            MirrorConfigEntity mirrorconfig = mirrorConfigModel.getByKey(mirrorId);
            if (Objects.isNull(mirrorconfig)) {
                throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403001, MirrorErrorCodeEnum.API_ERROR_403001.getMsg());
            }
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("mirrorId", mirrorId);
            param.put("del", DelEnum.NORMAL.getDel());
            param.put("orderByStr", "sort");
            List<MirrorMenuEntity> mirrormenuList = mirrorMenuModel.findEntitys(param);
            List<MirrorMenuEntity> parentMenuList = new ArrayList<>();
            Map<Long, List<MirrorMenuVO>> menuMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            for (MirrorMenuEntity entity : mirrormenuList) {
                if (Objects.equals(entity.getParentId(), 0L)) {
                    // 一级菜单
                    parentMenuList.add(entity);
                } else {
                    // 二级菜单
                    if (Objects.isNull(menuMap.get(entity.getParentId()))) {
                        List<MirrorMenuVO> childMenuList = new ArrayList<>();
                        MirrorMenuVO childMenu = new MirrorMenuVO();
                        BeanUtil.copyProperties(entity, childMenu);
                        childMenuList.add(childMenu);
                        menuMap.put(entity.getParentId(), childMenuList);
                    } else {
                        List<MirrorMenuVO> childMenuList = menuMap.get(entity.getParentId());
                        MirrorMenuVO childMenu = new MirrorMenuVO();
                        BeanUtil.copyProperties(entity, childMenu);
                        childMenuList.add(childMenu);
                        menuMap.put(entity.getParentId(), childMenuList);
                    }
                }
            }
            List<MirrorMenuVO> parentList = new ArrayList<>();
            parentMenuList.forEach(item->{
                MirrorMenuVO mirrorMenuVO = new MirrorMenuVO();
                BeanUtil.copyProperties(item, mirrorMenuVO);
                mirrorMenuVO.setSubMenuList(Objects.nonNull(menuMap.get(item.getId())) ? menuMap.get(item.getId()) : new ArrayList<MirrorMenuVO>());
                parentList.add(mirrorMenuVO);
            });
            mirrorMenuListVO.setMenuList(parentList);
        } catch (XbbException e) {
            throw e;
        } catch (Exception e) {
            LOG.error("getMenuList error: ", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        return mirrorMenuListVO;
    }

    /**
     * 组合镜像菜单列表
     * @param mirrorMenuListDTO
     * @return
     * @throws XbbException
     * @author xingxing.xiao
     */
    @Override
    public MirrorMenuListVO getMenuListAll(MirrorMenuListAllDTO mirrorMenuListDTO) throws XbbException {
        MirrorMenuListVO mirrorMenuListVO = new MirrorMenuListVO();
        try {
            Long appModuleId = mirrorMenuListDTO.getAppModuleId();
            AppModuleEntity appEntity = appModuleModel.getByKey(appModuleId);
            if (appEntity == null) {
                throw new XbbException(TemplateCenterErrorCodeEnum.API_ERROR_402005);
            }
            List<Long> mirrIdIn = JSONObject.parseArray(appEntity.getMirrorId().toJSONString(), Long.class);
            Map<String, Object> configParam = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            configParam.put("idIn", mirrIdIn);
            configParam.put("del", DelEnum.NORMAL.getDel());
            List<MirrorConfigEntity> mirrorConfigList = mirrorConfigModel.findEntitys(configParam);
            if (mirrorConfigList.isEmpty()) {
                throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403001);
            }

            // 去除系统级应用
            Map<String, Object> appParam = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            appParam.put("mirrorIdIn", mirrIdIn);
            appParam.put("del", DelEnum.NORMAL.getDel());
            List<MirrorAppEntity> mirrorAppList = mirrorAppModel.findEntitys(appParam);
            mirrorAppList.forEach(item -> {
                if (Objects.equals(item.getSaasMark(), SaasMarkEnum.SAAS.getCode())) {
                    mirrIdIn.remove(item.getMirrorId());
                }
            });

            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("mirrorIdIn", mirrIdIn);
            param.put("del", DelEnum.NORMAL.getDel());
            param.put("orderByStr", "sort");
            List<MirrorMenuEntity> mirrormenuList = mirrorMenuModel.findEntitys(param);

            List<MirrorMenuEntity> parentMenuList = new ArrayList<>();
            Map<Long, List<MirrorMenuVO>> menuMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            for (MirrorMenuEntity entity : mirrormenuList) {
                if (Objects.equals(entity.getParentId(), 0L)) {
                    // 一级菜单
                    parentMenuList.add(entity);
                } else {
                    // 二级菜单
                    if (Objects.isNull(menuMap.get(entity.getParentId()))) {
                        List<MirrorMenuVO> childMenuList = new ArrayList<>();
                        MirrorMenuVO childMenu = new MirrorMenuVO();
                        BeanUtil.copyProperties(entity, childMenu);
                        childMenuList.add(childMenu);
                        menuMap.put(entity.getParentId(), childMenuList);
                    } else {
                        List<MirrorMenuVO> childMenuList = menuMap.get(entity.getParentId());
                        MirrorMenuVO childMenu = new MirrorMenuVO();
                        BeanUtil.copyProperties(entity, childMenu);
                        childMenuList.add(childMenu);
                        menuMap.put(entity.getParentId(), childMenuList);
                    }
                }
            }
            List<MirrorMenuVO> parentList = new ArrayList<>();
            parentMenuList.forEach(item->{
                MirrorMenuVO mirrorMenuVO = new MirrorMenuVO();
                BeanUtil.copyProperties(item, mirrorMenuVO);
                mirrorMenuVO.setSubMenuList(Objects.nonNull(menuMap.get(item.getId())) ? menuMap.get(item.getId()) : new ArrayList<MirrorMenuVO>());
                parentList.add(mirrorMenuVO);
            });
            mirrorMenuListVO.setMenuList(parentList);
        } catch (XbbException e) {
            throw e;
        } catch (Exception e) {
            LOG.error("getMenuListAll error: ", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        return mirrorMenuListVO;
    }

    /**
     * 工单镜像列表
     * @param mirrorMenuListDTO
     * @return
     * @throws XbbException
     * @author xingxing.xiao
     */
    @Override
    public MirrorWorkOrderListVO getWorkOrderList(MirrorMenuListDTO mirrorMenuListDTO) throws XbbException {
        MirrorWorkOrderListVO mirrorWorkOrderListVO = new MirrorWorkOrderListVO();
        try {
            Long mirrorId = mirrorMenuListDTO.getMirrorId();
            MirrorConfigEntity mirrorconfig = mirrorConfigModel.getByKey(mirrorId);
            if (Objects.isNull(mirrorconfig)) {
                throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403001, MirrorErrorCodeEnum.API_ERROR_403001.getMsg());
            }
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("mirrorId", mirrorId);
            param.put("del", DelEnum.NORMAL.getDel());
            param.put("orderByStr", "sort");
            List<MirrorWorkOrderTemplateCategoryEntity> workOrderCategoryList = mirrorWorkOrderTemplateCategoryModel.findEntitys(param);
            param.clear();
            param.put("mirrorId", mirrorId);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorWorkOrderTemplateEntity> workOrderTemplateList = mirrorWorkOrderTemplateModel.findEntitys(param);
            Map<Long, List<WorkOrderTemplateVO>> templateMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            workOrderTemplateList.forEach(item -> {
                if (Objects.isNull(templateMap.get(item.getCategoryId()))) {
                    List<WorkOrderTemplateVO> templateList = new ArrayList<>();
                    WorkOrderTemplateVO workOrderTemplateVO = new WorkOrderTemplateVO(item.getId(), item.getName(), item.getMirrorId(), item.getAppId(), item.getEnable());
                    templateList.add(workOrderTemplateVO);
                    templateMap.put(item.getCategoryId(), templateList);
                } else {
                    List<WorkOrderTemplateVO> templateList = templateMap.get(item.getCategoryId());
                    WorkOrderTemplateVO workOrderTemplateVO = new WorkOrderTemplateVO(item.getId(), item.getName(), item.getMirrorId(), item.getAppId(), item.getEnable());
                    templateList.add(workOrderTemplateVO);
                    templateMap.put(item.getCategoryId(), templateList);
                }
            });
            List<WorkOrderCategoryVO> list = new ArrayList<>();
            workOrderCategoryList.forEach(item -> {
                WorkOrderCategoryVO workOrderCategoryVO = new WorkOrderCategoryVO();
                workOrderCategoryVO.setId(item.getId());
                workOrderCategoryVO.setName(item.getName());
                workOrderCategoryVO.setMirrorId(item.getMirrorId());
                workOrderCategoryVO.setAppId(item.getAppId());
                workOrderCategoryVO.setTemplateList(Objects.nonNull(templateMap.get(item.getId())) ? templateMap.get(item.getId()) : new ArrayList<>());
                list.add(workOrderCategoryVO);
            });
            mirrorWorkOrderListVO.setList(list);
        }  catch (XbbException e) {
            throw e;
        } catch (Exception e) {
            LOG.error("getChartList error: ", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        return mirrorWorkOrderListVO;
    }

    /**
     * 工单镜像列表
     * @param mirrorMenuListDTO
     * @return
     * @throws XbbException
     * @author xingxing.xiao
     */
    @Override
    public MirrorWorkOrderListVO getWorkOrderListAll(MirrorMenuListAllDTO mirrorMenuListDTO) throws XbbException {
        MirrorWorkOrderListVO mirrorWorkOrderListVO = new MirrorWorkOrderListVO();
        try {
            Long appModuleId = mirrorMenuListDTO.getAppModuleId();
            AppModuleEntity appEntity = appModuleModel.getByKey(appModuleId);
            if (appEntity == null) {
                throw new XbbException(TemplateCenterErrorCodeEnum.API_ERROR_402005);
            }
            List<Long> mirrIdIn = JSONObject.parseArray(appEntity.getMirrorId().toJSONString(), Long.class);
            Map<String, Object> configParam = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            configParam.put("idIn", mirrIdIn);
            configParam.put("del", DelEnum.NORMAL.getDel());
            List<MirrorConfigEntity> mirrorConfigList = mirrorConfigModel.findEntitys(configParam);
            if (mirrorConfigList.isEmpty()) {
                throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403001);
            }

            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("mirrorIdIn", mirrIdIn);
            param.put("del", DelEnum.NORMAL.getDel());
            param.put("orderByStr", "sort");
            List<MirrorWorkOrderTemplateCategoryEntity> workOrderCategoryList = mirrorWorkOrderTemplateCategoryModel.findEntitys(param);
            param.clear();
            param.put("mirrorIdIn", mirrIdIn);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorWorkOrderTemplateEntity> workOrderTemplateList = mirrorWorkOrderTemplateModel.findEntitys(param);
            Map<Long, List<WorkOrderTemplateVO>> templateMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            workOrderTemplateList.forEach(item -> {
                if (Objects.isNull(templateMap.get(item.getCategoryId()))) {
                    List<WorkOrderTemplateVO> templateList = new ArrayList<>();
                    WorkOrderTemplateVO workOrderTemplateVO = new WorkOrderTemplateVO(item.getId(), item.getName(), item.getMirrorId(), item.getAppId(), item.getEnable());
                    templateList.add(workOrderTemplateVO);
                    templateMap.put(item.getCategoryId(), templateList);
                } else {
                    List<WorkOrderTemplateVO> templateList = templateMap.get(item.getCategoryId());
                    WorkOrderTemplateVO workOrderTemplateVO = new WorkOrderTemplateVO(item.getId(), item.getName(), item.getMirrorId(), item.getAppId(), item.getEnable());
                    templateList.add(workOrderTemplateVO);
                    templateMap.put(item.getCategoryId(), templateList);
                }
            });
            List<WorkOrderCategoryVO> list = new ArrayList<>();
            workOrderCategoryList.forEach(item -> {
                WorkOrderCategoryVO workOrderCategoryVO = new WorkOrderCategoryVO();
                workOrderCategoryVO.setId(item.getId());
                workOrderCategoryVO.setName(item.getName());
                workOrderCategoryVO.setMirrorId(item.getMirrorId());
                workOrderCategoryVO.setAppId(item.getAppId());
                workOrderCategoryVO.setTemplateList(Objects.nonNull(templateMap.get(item.getId())) ? templateMap.get(item.getId()) : new ArrayList<>());
                list.add(workOrderCategoryVO);
            });
            mirrorWorkOrderListVO.setList(list);
        }  catch (XbbException e) {
            throw e;
        } catch (Exception e) {
            LOG.error("getChartList error: ", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        return mirrorWorkOrderListVO;
    }

    /**
     * 表单镜像列表（展示菜单 - 表单）
     * @param mirrorMenuListDTO
     * @return
     * @throws XbbException
     * @author xingxing.xiao
     */
    @Override
    public MirrorSystemMenuListVO getSystemFormList(MirrorMenuListDTO mirrorMenuListDTO) throws XbbException {
        MirrorSystemMenuListVO mirrorMenuListVO = new MirrorSystemMenuListVO();
        try {
            Long mirrorId = mirrorMenuListDTO.getMirrorId();
            MirrorConfigEntity mirrorconfig = mirrorConfigModel.getByKey(mirrorId);
            if (Objects.isNull(mirrorconfig)) {
                throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403001, MirrorErrorCodeEnum.API_ERROR_403001.getMsg());
            }
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("mirrorId", mirrorId);
            param.put("del", DelEnum.NORMAL.getDel());
            param.put("orderByStr", "sort");
            List<MirrorMenuEntity> menuList = mirrorMenuModel.findEntitys(param);
            List<MirrorMenuEntity> parentMenuList = new ArrayList<>();
            List<Long> parentMenuIds = new ArrayList<>();
            Map<Long, List<MirrorMenuEntity>> childMenuMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            for (MirrorMenuEntity entity : menuList) {
                Long parentId = entity.getParentId();
                if (Objects.equals(parentId, 0L)) {
                    parentMenuList.add(entity);
                    boolean flag = Objects.equals(entity.getAlias(), XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getAlias()) || Objects.equals(entity.getAlias(), XbbRefTypeEnum.SALES_OPPORTUNITY.getAlias()) || Objects.equals(entity.getAlias(), XbbRefTypeEnum.CLUE.getAlias());
                    if (flag) {
                        parentMenuIds.add(entity.getId());
                    }
                } else {
                    if (Objects.isNull(childMenuMap.get(parentId))) {
                        List<MirrorMenuEntity> childMenuList = new ArrayList<>();
                        childMenuList.add(entity);
                        childMenuMap.put(parentId, childMenuList);
                    } else {
                        List<MirrorMenuEntity> childMenuList = childMenuMap.get(parentId);
                        childMenuList.add(entity);
                        childMenuMap.put(parentId, childMenuList);
                    }
                }
            }
            // 把客户和机会的子菜单删除
            parentMenuIds.forEach(childMenuMap::remove);

            param.clear();
            param.put("mirrorId", mirrorId);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorFormEntity> formList = mirrorFormModel.findEntitys(param);
            Map<Long, List<MirrorFormVO>> mirrorFormMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            for (MirrorFormEntity formEntity : formList) {
                Long mirrorMenuId = formEntity.getMirrorMenuId();
                if (Objects.isNull(mirrorFormMap.get(mirrorMenuId))) {
                    List<MirrorFormVO> mirrorFormList = new ArrayList<>();
                    MirrorFormVO formVO = new MirrorFormVO();
                    BeanUtil.copyProperties(formEntity, formVO);
                    mirrorFormList.add(formVO);
                    mirrorFormMap.put(mirrorMenuId, mirrorFormList);
                } else {
                    List<MirrorFormVO> mirrorFormList = mirrorFormMap.get(mirrorMenuId);
                    MirrorFormVO formVO = new MirrorFormVO();
                    BeanUtil.copyProperties(formEntity, formVO);
                    mirrorFormList.add(formVO);
                    mirrorFormMap.put(mirrorMenuId, mirrorFormList);
                }
            }

            List<MirrorSystemMenuVO> mirrorMenuList = new ArrayList<>();
            parentMenuList.forEach(item -> {
                MirrorSystemMenuVO mirrorMenuVO = new MirrorSystemMenuVO();
                BeanUtil.copyProperties(item, mirrorMenuVO);
                if (Objects.nonNull(childMenuMap.get(item.getId()))) {
                    List<MirrorFormVO> mirrorFormByMenuList = new ArrayList<>();
                    List<MirrorMenuEntity> mirrorChildMenuList = childMenuMap.get(item.getId());
                    for (MirrorMenuEntity entity : mirrorChildMenuList) {
                        List<MirrorFormVO> mirrorFormVOList = mirrorFormMap.get(entity.getId());
                        if (Objects.nonNull(mirrorFormVOList)) {
                            // 要判空，例：模拟生产就没有表单
                            mirrorFormByMenuList.addAll(mirrorFormVOList);
                        }
                    }
                    mirrorMenuVO.setIsParent(1);
                    mirrorMenuVO.setSubFormList(mirrorFormByMenuList);
                } else {
                    List<String> multiFormAliasList = Arrays.asList(XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getAlias(),XbbRefTypeEnum.CONTRACT.getAlias(),XbbRefTypeEnum.SALES_OPPORTUNITY.getAlias()
                                                            ,XbbRefTypeEnum.WORK_ORDER_V2.getAlias(),XbbRefTypeEnum.RECEIPT_ORDER.getAlias());
                    if (multiFormAliasList.contains(item.getAlias())) {
                        mirrorMenuVO.setIsParent(1);
                        mirrorMenuVO.setSubFormList(Objects.nonNull(mirrorFormMap.get(item.getId())) ? mirrorFormMap.get(item.getId()) : new ArrayList<>());
                    } else {
                        List<MirrorFormVO> mirrorFormVOList = mirrorFormMap.get(item.getId());
                        if (CollectionsUtil.isEmpty(mirrorFormVOList)) {
                            return;
                        }
                        MirrorFormVO mirrorFormVO = mirrorFormVOList.get(0);
                        mirrorMenuVO.setId(mirrorFormVO.getId());
                        mirrorMenuVO.setName(mirrorFormVO.getName());
                        mirrorMenuVO.setIsParent(0);
                        mirrorMenuVO.setSubFormList(new ArrayList<>());
                    }
                }
                mirrorMenuList.add(mirrorMenuVO);
            });
            mirrorMenuListVO.setMenuList(mirrorMenuList);
        } catch (XbbException e) {
            throw e;
        } catch (Exception e) {
            LOG.error("getChartList error: ", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        return mirrorMenuListVO;
    }

    /**
     * 组合表单镜像列表（展示菜单 - 表单）
     * @param mirrorMenuListAllDTO
     * @return
     * @throws XbbException
     * @author xingxing.xiao
     */
    @Override
    public MirrorSystemMenuListVO getSystemFormListAll(MirrorMenuListAllDTO mirrorMenuListAllDTO) throws XbbException {
        MirrorSystemMenuListVO mirrorMenuListVO = new MirrorSystemMenuListVO();
        try {
            Long appModuleId = mirrorMenuListAllDTO.getAppModuleId();
            AppModuleEntity appEntity = appModuleModel.getByKey(appModuleId);
            if (appEntity == null) {
                throw new XbbException(TemplateCenterErrorCodeEnum.API_ERROR_402005);
            }
            List<Long> mirrIdIn = JSONObject.parseArray(appEntity.getMirrorId().toJSONString(), Long.class);
            Map<String, Object> configParam = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            configParam.put("idIn", mirrIdIn);
            configParam.put("del", DelEnum.NORMAL.getDel());
            List<MirrorConfigEntity> mirrorConfigList = mirrorConfigModel.findEntitys(configParam);
            if (mirrorConfigList.isEmpty()) {
                throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403001);
            }

            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("mirrorIdIn", mirrIdIn);
            param.put("del", DelEnum.NORMAL.getDel());
            param.put("orderByStr", "sort");
            List<MirrorMenuEntity> menuList = mirrorMenuModel.findEntitys(param);

            List<MirrorMenuEntity> parentMenuList = new ArrayList<>();
            List<Long> parentMenuIds = new ArrayList<>();
            Map<Long, List<MirrorMenuEntity>> childMenuMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            for (MirrorMenuEntity entity : menuList) {
                Long parentId = entity.getParentId();
                if (Objects.equals(parentId, 0L)) {
                    parentMenuList.add(entity);
                    boolean flag = Objects.equals(entity.getAlias(), XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getAlias()) || Objects.equals(entity.getAlias(), XbbRefTypeEnum.SALES_OPPORTUNITY.getAlias());
                    if (flag) {
                        parentMenuIds.add(entity.getId());
                    }
                } else {
                    if (Objects.isNull(childMenuMap.get(parentId))) {
                        List<MirrorMenuEntity> childMenuList = new ArrayList<>();
                        childMenuList.add(entity);
                        childMenuMap.put(parentId, childMenuList);
                    } else {
                        List<MirrorMenuEntity> childMenuList = childMenuMap.get(parentId);
                        childMenuList.add(entity);
                        childMenuMap.put(parentId, childMenuList);
                    }
                }
            }
            // 把客户和机会的子菜单删除
            parentMenuIds.forEach(childMenuMap::remove);

            param.clear();
            param.put("mirrorIdIn", mirrIdIn);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorFormEntity> formList = mirrorFormModel.findEntitys(param);
            Map<Long, List<MirrorFormVO>> mirrorFormMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            for (MirrorFormEntity formEntity : formList) {
                Long mirrorMenuId = formEntity.getMirrorMenuId();
                if (Objects.isNull(mirrorFormMap.get(mirrorMenuId))) {
                    List<MirrorFormVO> mirrorFormList = new ArrayList<>();
                    MirrorFormVO formVO = new MirrorFormVO();
                    BeanUtil.copyProperties(formEntity, formVO);
                    mirrorFormList.add(formVO);
                    mirrorFormMap.put(mirrorMenuId, mirrorFormList);
                } else {
                    List<MirrorFormVO> mirrorFormList = mirrorFormMap.get(mirrorMenuId);
                    MirrorFormVO formVO = new MirrorFormVO();
                    BeanUtil.copyProperties(formEntity, formVO);
                    mirrorFormList.add(formVO);
                    mirrorFormMap.put(mirrorMenuId, mirrorFormList);
                }
            }

            List<MirrorSystemMenuVO> mirrorMenuList = new ArrayList<>();
            parentMenuList.forEach(item -> {
                MirrorSystemMenuVO mirrorMenuVO = new MirrorSystemMenuVO();
                BeanUtil.copyProperties(item, mirrorMenuVO);
                if (Objects.nonNull(childMenuMap.get(item.getId()))) {
                    List<MirrorFormVO> mirrorFormByMenuList = new ArrayList<>();
                    List<MirrorMenuEntity> mirrorChildMenuList = childMenuMap.get(item.getId());
                    for (MirrorMenuEntity entity : mirrorChildMenuList) {
                        List<MirrorFormVO> mirrorFormVOList = mirrorFormMap.get(entity.getId());
                        if (Objects.nonNull(mirrorFormVOList)) {
                            // 要判空，例：模拟生产就没有表单
                            mirrorFormByMenuList.addAll(mirrorFormVOList);
                        }
                    }
                    mirrorMenuVO.setIsParent(1);
                    mirrorMenuVO.setSubFormList(mirrorFormByMenuList);
                } else {
                    if (Objects.equals(item.getAlias(), XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getAlias()) || Objects.equals(item.getAlias(), XbbRefTypeEnum.CONTRACT.getAlias()) || Objects.equals(item.getAlias(), XbbRefTypeEnum.SALES_OPPORTUNITY.getAlias())) {
                        mirrorMenuVO.setIsParent(1);
                        mirrorMenuVO.setSubFormList(Objects.nonNull(mirrorFormMap.get(item.getId())) ? mirrorFormMap.get(item.getId()) : new ArrayList<>());
                    } else {
                        List<MirrorFormVO> mirrorFormVOList = mirrorFormMap.get(item.getId());
                        if(CollectionUtils.isNotEmpty(mirrorFormVOList)){
                            MirrorFormVO mirrorFormVO = mirrorFormVOList.get(0);
                            mirrorMenuVO.setId(mirrorFormVO.getId());
                            mirrorMenuVO.setName(mirrorFormVO.getName());
                            mirrorMenuVO.setIsParent(0);
                            mirrorMenuVO.setSubFormList(new ArrayList<>());
                        }
                    }
                }
                mirrorMenuList.add(mirrorMenuVO);
            });
            mirrorMenuListVO.setMenuList(mirrorMenuList);
        } catch (XbbException e) {
            throw e;
        } catch (Exception e) {
            LOG.error("getChartListAll error: ", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        return mirrorMenuListVO;
    }

    /**
     * 图表镜像列表
     * @param mirrorMenuListDTO
     * @return
     * @throws XbbException
     * @author xingxing.xiao
     */
    @Override
    public MirrorChartListVO getChartList(MirrorMenuListDTO mirrorMenuListDTO) throws XbbException {
        MirrorChartListVO mirrorChartListVO = new MirrorChartListVO();
        try {
            Long mirrorId = mirrorMenuListDTO.getMirrorId();
            MirrorConfigEntity mirrorconfig = mirrorConfigModel.getByKey(mirrorId);
            if (Objects.isNull(mirrorconfig)) {
                throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403001, MirrorErrorCodeEnum.API_ERROR_403001.getMsg());
            }
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("mirrorId", mirrorId);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorChartCategoryEntity> mirrorCategoryList = mirrorChartCategoryModel.findEntitys(param);
            List<ChartMenuVO> categoryList = new ArrayList<>();
            mirrorCategoryList.forEach(item -> {
                // 排除父分类
                if (!Objects.equals(item.getParentId(), 0L)) {
                    ChartMenuVO chartMenuVO = new ChartMenuVO();
                    BeanUtil.copyProperties(item, chartMenuVO);
                    categoryList.add(chartMenuVO);
                }
            });
            mirrorChartListVO.setList(categoryList);
        }  catch (XbbException e) {
            throw e;
        } catch (Exception e) {
            LOG.error("getChartList error: ", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        return mirrorChartListVO;
    }

    /**
     * 组合图表镜像列表
     * @param mirrorMenuListDTO
     * @return
     * @throws XbbException
     * @author xingxing.xiao
     */
    @Override
    public MirrorChartListAllVO getChartListAll(MirrorMenuListAllDTO mirrorMenuListDTO) throws XbbException {
        MirrorChartListAllVO mirrorChartListVO = new MirrorChartListAllVO();
        try {
            Long appModuleId = mirrorMenuListDTO.getAppModuleId();
            AppModuleEntity entity = appModuleModel.getByKey(appModuleId);
            if (entity == null) {
                throw new XbbException(TemplateCenterErrorCodeEnum.API_ERROR_402005);
            }
            List<Long> mirrIdIn = JSONObject.parseArray(entity.getMirrorId().toJSONString(), Long.class);
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("idIn", mirrIdIn);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorConfigEntity> mirrorConfigList = mirrorConfigModel.findEntitys(param);
            if (mirrorConfigList.isEmpty()) {
                throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403001);
            }
            Map<String, Object> mirrorChartParam = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            mirrorChartParam.put("mirrorIdIn", mirrIdIn);
            mirrorChartParam.put("del", DelEnum.NORMAL.getDel());
            List<MirrorChartCategoryEntity> mirrorCategoryList = mirrorChartCategoryModel.findEntitys(mirrorChartParam);
            List<ChartMenuVO> categoryList = new ArrayList<>();
            mirrorCategoryList.forEach(item -> {
                // 排除父分类
                if (!Objects.equals(item.getParentId(), 0L)) {
                    ChartMenuVO chartMenuVO = new ChartMenuVO();
                    BeanUtil.copyProperties(item, chartMenuVO);
                    categoryList.add(chartMenuVO);
                }
            });
            mirrorChartListVO.setList(categoryList);
        }  catch (XbbException e) {
            throw e;
        } catch (Exception e) {
            LOG.error("getChartListAll error: ", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
        }
        return mirrorChartListVO;
    }

    /**
     * 镜像预览
     * @param mirrorPreviewDTO
     * @return
     * @throws XbbException
     * @author xingxing.xiao
     */
    @Override
    public MirrorPreviewVO preview(MirrorPreviewDTO mirrorPreviewDTO) throws XbbException {
        MirrorPreviewVO mirrorPreviewVO = new MirrorPreviewVO();
        Long mirrorMenuId = mirrorPreviewDTO.getMirrorMenuId();
        Long mirrorId = mirrorPreviewDTO.getMirrorId();
        String applicationType = mirrorPreviewDTO.getApplicationType();
        try {
            Long mirrorFormId;
            if (Objects.equals(applicationType, MirrorApplicationTypeEnum.WORK_ORDER.getType())) {
                FormBindTemplateInfoEntity bindEntity = formBindTemplateInfoModel.getByTemplateId(mirrorMenuId, FormBindTemplateTypeEnum.MIRROR.getType());
                if (Objects.isNull(bindEntity)) {
                    throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403002, MirrorErrorCodeEnum.API_ERROR_403002.getMsg());
                }
                mirrorFormId = bindEntity.getFormId();
            } else if (Objects.equals(applicationType, MirrorApplicationTypeEnum.SYSTEM.getType())) {
                mirrorFormId = mirrorMenuId;
            } else {
                MirrorFormEntity mirrorFormEntity = mirrorFormModel.getByMirrorMenuId(mirrorMenuId, mirrorId);
                if (Objects.isNull(mirrorFormEntity)) {
                    throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403002, MirrorErrorCodeEnum.API_ERROR_403002.getMsg());
                }
                mirrorFormId = mirrorFormEntity.getId();
            }
            MirrorFormExplainEntity formExplainEntity = mirrorFormExplainModel.getByMirrorFormId(mirrorFormId);
            // 预览暂时不做处理
            String explains = formExplainEntity.getExplains();
            List<FieldAttrEntity> explainList =  JSONArray.parseArray(explains, FieldAttrEntity.class);
            mirrorPreviewVO.setExplainList(explainList);
            // 手机号码等正则枚举类
            mirrorPreviewVO.setPatternList(PatternEnum.list());
            // 日期枚举类
            mirrorPreviewVO.setDateTimeList(DateTimeEnum.list());
            // 线的枚举类
            mirrorPreviewVO.setSeparatorList(SeparatorEnum.list());
        } catch (XbbException e) {
            throw e;
        } catch (Exception e) {
            LOG.error("installSignApp error: ", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        return mirrorPreviewVO;
    }

    /**
     * 镜像图表预览
     * @param mirrorChartPreviewDTO
     * @return
     * @throws XbbException
     * @author xingxing.xiao
     */
    @Override
    public MirrorChartPreviewVO getChartPreview(MirrorChartPreviewDTO mirrorChartPreviewDTO) throws XbbException {
        MirrorChartPreviewVO mirrorChartPreviewVO = new MirrorChartPreviewVO();
        Long mirrorId = mirrorChartPreviewDTO.getMirrorId();
        Long categoryId = mirrorChartPreviewDTO.getCategoryId();
        try {
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("mirrorId", mirrorId);
            param.put("categoryId", categoryId);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorChartEntity> mirrorChartList = mirrorChartModel.findEntitys(param);
            if(mirrorChartList == null) {
                throw new XbbException(StatisticsServicesErrorCodeEnum.API_ERROR_224012);
            }
            List<MirrorChartPojoVO> chartList = new ArrayList<>();
            for (MirrorChartEntity entity : mirrorChartList) {
                MirrorChartPojoVO chartPojoVO = new MirrorChartPojoVO();
                BeanUtil.copyProperties(entity, chartPojoVO);
                JSONArray search = entity.getSearch();
                if (search != null && !search.isEmpty()) {
                    chartPojoVO.setHasSearch(1);
                    chartPojoVO.setSearch(search);
                }
                JSONArray summaryAttr = entity.getSummaryAttr();
                if (summaryAttr != null && !summaryAttr.isEmpty()) {
                    chartPojoVO.setSummaryAttr(summaryAttr);
                }
                //表格无法发布到首页
                Integer chartType = entity.getChartType();
                boolean ifTable = Objects.equals(chartType, ChartTypeEnum.TABLE.getCode()) ||
                        Objects.equals(chartType, ChartTypeEnum.DATA_QUERY_TABLE.getCode()) ||
                        Objects.equals(chartType, ChartTypeEnum.DATA_STATISTICS_TABLE.getCode());
                if (ifTable) {
                    // 参考 ChartPublishEnum
                    chartPojoVO.setPublish(2);
                } else {
                    chartPojoVO.setPublish(0);
                }
                // 默认展示可导出(参考ChartPermissionsTypeEnum)
                chartPojoVO.setIsExport(1);
                // 将图表置入参数和数据
                MirrorChartDataVO chartDataVO = dealwithChartData(entity);
                chartPojoVO.setChartData(chartDataVO);
                chartList.add(chartPojoVO);
            }
            mirrorChartPreviewVO.setChartList(chartList);
        } catch (XbbException e) {
            throw e;
        } catch (Exception e) {
            LOG.error("installSignApp error: ", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        return mirrorChartPreviewVO;
    }

    /**
     * 图表置入参数和数据
     * @param chartEntity
     * @author xingxing.xiao
     */
    private MirrorChartDataVO dealwithChartData (MirrorChartEntity chartEntity) throws XbbException {
        MirrorChartDataVO chartDataVO = new MirrorChartDataVO();
        chartDataVO.setName(chartEntity.getName());
        chartDataVO.setMemo(chartEntity.getMemo());
        chartDataVO.setChartType(chartEntity.getChartType());
        // 自定义图表镜像数据
        Map<Long, String> tableLocationMap = mirrorChartData(chartEntity);
        PageHelper pageHelper = new PageHelper(1);
        // 镜像图表渲染
        JSONObject result = mirrorChartRender(chartEntity, tableLocationMap, pageHelper);
        if(chartEntity.getChartType().equals(ChartTypeEnum.DATA_QUERY_TABLE.getCode())
                ||chartEntity.getChartType().equals(ChartTypeEnum.DATA_STATISTICS_TABLE.getCode())){
            ResultTableVO resultTableVO = new ResultTableVO();
            if(result!=null && result.size() > 0){
                resultTableVO = JSONObject.parseObject(result.toJSONString(),ResultTableVO.class);
            }
            resultTableVO.setPageHelper(pageHelper);
            resultTableVO.setTableName(chartEntity.getName());
            resultTableVO.setChartType(chartEntity.getChartType());
            chartDataVO.setTable(resultTableVO);
        }else if (chartEntity.getChartType().equals(ChartTypeEnum.BAR_CHART.getCode())
                ||chartEntity.getChartType().equals(ChartTypeEnum.LINE_CHART.getCode())
                ||chartEntity.getChartType().equals(ChartTypeEnum.AREA_CHART.getCode())
                ||chartEntity.getChartType().equals(ChartTypeEnum.RADAR_CHART.getCode())
                ||chartEntity.getChartType().equals(ChartTypeEnum.PIE_CHART.getCode())){
            MirrorResultChartVO resultChartVO = new MirrorResultChartVO();
            if(result!=null && result.size() > 0){
                resultChartVO = JSONObject.parseObject(result.toJSONString(),MirrorResultChartVO.class);
            }
            List<MirrorResultChartVO> chartVOList = new ArrayList<>();
            chartVOList.add(resultChartVO);
            chartDataVO.setChart(chartVOList);
        }
        return  chartDataVO;
    }

    /**
     * 自定义图表镜像数据(使用假数据)
     * @param chartEntity
     * @author xingxing.xiao
     */
    private Map<Long,String> mirrorChartData (MirrorChartEntity chartEntity) {
        Map<Long,String> tableLocationMap= new HashMap<>(16);
        //获取主数据源和从数据源
        JSONObject driverSourcesObject = chartEntity.getDriverSources();
        SlaveSourcePojo driverSourcePojo = JSON.toJavaObject(driverSourcesObject, SlaveSourcePojo.class);
        JSONArray slaveSourcesObject = chartEntity.getSlaveSources();
        // 从数据表，多表数据源（sign: 0 多表  1 单表，slaveSources没有数据  ）
        if (!slaveSourcesObject.isEmpty()) {

        } else {
            // 单表
        }

        return  tableLocationMap;
    }

    /**
     * 图表镜像渲染
     * @author xingxing.xiao
     */
    private JSONObject mirrorChartRender(MirrorChartEntity chartEntity, Map<Long,String> tableLocationMap, PageHelper pageHelper) throws XbbException{
        JSONObject result = null;
        //根据图表类型的不同渲染格式也不同
        Integer chartType = chartEntity.getChartType();
        ChartTypeEnum chartTypeEnum = ChartTypeEnum.getByCode(chartType);
        if(chartTypeEnum != null) {
            switch (chartTypeEnum) {
                case DATA_QUERY_TABLE:
                    if(chartEntity.getDisplayAttr()==null || chartEntity.getDisplayAttr().size()==0){
                        result = new JSONObject();
                        List list = new ArrayList<>();
                        result.put("customTitle",list);
                        result.put("customData",list);
                        int rowCount = 0;
                        pageHelper.setRowsCount(rowCount);
                        break;
                    }
                    result = mirrorChartQueryTable(chartEntity, tableLocationMap, pageHelper);
                    break;
                case DATA_STATISTICS_TABLE:
                    result = mirrorChartStatisticsTable(chartEntity, tableLocationMap, pageHelper);
                    break;
                case BAR_CHART:
                case LINE_CHART:
                case AREA_CHART:
                case RADAR_CHART:
                case PIE_CHART:
                    result = mirrorChartQuery(chartEntity, tableLocationMap);
                    break;
                default:
                    break;
            }
        }

        return result;
    }

    /**
     * 数据查询表
     * @author xingxing.xiao
     */
    private JSONObject mirrorChartQueryTable(MirrorChartEntity chartEntity, Map<Long,String> tableLocationMap, PageHelper pageHelper) throws XbbException {
        JSONObject result = new JSONObject();
        // 注：镜像没有数据应该不需要筛选条件
        JSONArray tableHead = new JSONArray();
        Map<JSONObject,FieldAttrPojo> parseAttrMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        List<FieldAttrPojo> fieldAttrPojoList = JSONArray.parseArray(chartEntity.getDisplayAttr().toJSONString(),FieldAttrPojo.class);
        // 处理查询表和统计表展示字段
        displayMirrorAttr(fieldAttrPojoList, null, tableLocationMap, tableHead, parseAttrMap);

        List<JSONObject> headList = JSONObject.parseArray(tableHead.toJSONString(),JSONObject.class);
        result.put("customTitle",headList);

        // 暂定不展示值
        result.put("customData",new ArrayList<>());
        result.put("realRowsCount", 0);
        /*int rowCount = 0;
        pageHelper.setRowsCount(rowCount);*/

        return result;
    }

    /**
     * 数据统计表
     * @author xingxing.xiao
     */
    private JSONObject mirrorChartStatisticsTable(MirrorChartEntity chartEntity, Map<Long,String> tableLocationMap, PageHelper pageHelper) throws XbbException {
        JSONObject result = new JSONObject();

        JSONArray rowGroupAttr = chartEntity.getRowGroupAttr();
        JSONArray colGroupAttr = chartEntity.getColGroupAttr();
        JSONArray summaryAttr = chartEntity.getSummaryAttr();
        List<FieldAttrPojo> rowGroupAttrList = new ArrayList<>();
        List<FieldAttrPojo> summaryAttrList = new ArrayList<>();
        if (rowGroupAttr != null) {
            rowGroupAttrList = JSONArray.parseArray(rowGroupAttr.toJSONString(), FieldAttrPojo.class);
        }
        if (summaryAttr != null) {
            summaryAttrList = JSONArray.parseArray(summaryAttr.toJSONString(), FieldAttrPojo.class);
        }

        List<ChartStatisticsPojo> headChild = new ArrayList<>();
        if (colGroupAttr != null) {
            colGroupAttr.forEach(item -> {
                FieldAttrPojo fieldAttrPojo = JSONObject.parseObject(item.toString(), FieldAttrPojo.class);
                String labelName = fieldAttrPojo.getLabelName();
                String label = fieldAttrPojo.getLabel();
                String attrName = fieldAttrPojo.getAttrName();
                ChartStatisticsPojo pojo = new ChartStatisticsPojo();
                pojo.setKey(fieldAttrPojo.getAttr() + "&" + fieldAttrPojo.getFieldType());
                pojo.setValue(labelName);
                if (Objects.equals(labelName,label)) {
                    if (FieldTypeEnum.SUB_FORM.getType().intValue() == fieldAttrPojo.getFieldType()) {
                        pojo.setValue(attrName);
                    } else {
                        pojo.setValue(Arrays.asList(label.split("--")).get(0) + "--" + attrName);
                    }
                }
                pojo.setHead(true);
                pojo.setFormId(fieldAttrPojo.getFormId());
                if (fieldAttrPojo.getDateGroupType() != null) {
                    pojo.setDateGroupType(fieldAttrPojo.getDateGroupType());
                }
                headChild.add(pojo);
            });
        }

        //行分组的层级关系设置
        if (CollectionsUtil.isNotEmpty(rowGroupAttrList)) {
            ChartStatisticsPojo rowChartPojo = new ChartStatisticsPojo();
            row2setLevel(rowGroupAttrList, rowChartPojo, 0);
            headChild.add(rowChartPojo);
        } else if (CollectionUtils.isNotEmpty(summaryAttrList)){
            ChartStatisticsPojo pojo = new ChartStatisticsPojo();
            pojo.setKey("");
            pojo.setValue("");
            pojo.setHead(true);
            headChild.add(pojo);
        }

        JSONObject head = new JSONObject();
        result.put("rowsCount", 0);
        result.put("body", new ArrayList<>());
        head.put("child", headChild);
        result.put("head", head);
        result.put("colTotal", 0);

        return result;
    }

    /**
     * 图表展示
     * @return
     * @throws XbbException
     * @author xingxing.xiao
     */
    private JSONObject mirrorChartQuery(MirrorChartEntity chartEntity, Map<Long,String> tableLocationMap) throws XbbException {
        JSONObject result = new JSONObject();
        //分类 系列字段
        if (chartEntity.getChartClassify().size() > 0 && chartEntity.getChartValue().size() > 0) {

            JSONObject xAxisObject = chartEntity.getChartClassify().getJSONObject(0);
            FieldAttrPojo xAxisAttr = JSONObject.parseObject(xAxisObject.toJSONString(), FieldAttrPojo.class);

            // 三层聚合 不存在则为二层，2层聚合情况下chartValue值(字段名)作为系列
            JSONArray chartSeriesArray = chartEntity.getChartSeries();
            JSONObject legendObject = new JSONObject();
            if (chartSeriesArray != null && chartSeriesArray.size() > 0) {
                legendObject = chartEntity.getChartSeries().getJSONObject(0);
            }

            List<String> xAxis = new ArrayList<>();
            List<String> xAxisKeys = new ArrayList<>();
            List<String> legend = new ArrayList<>();
            List<String> legendKeys = new ArrayList<>();

            // 根据 fieldType 给假数据
            dealwithFieldType(xAxisAttr, xAxis, xAxisKeys);
            result.put("xAxis", xAxis);
            result.put("xAxisKeys", xAxisKeys);
            result.put("backXaxis", xAxis);
            result.put("backXaxisKeys", xAxisKeys);

            if (!legendObject.isEmpty()) {
                FieldAttrPojo legendAttr = JSONObject.parseObject(legendObject.toJSONString(), FieldAttrPojo.class);
                dealwithFieldType(legendAttr, legend, legendKeys);
            } else {
                // chartValue值(字段名) 作为系列
                legendObject = chartEntity.getChartValue().getJSONObject(0);
                FieldAttrPojo legendAttr = JSONObject.parseObject(legendObject.toJSONString(), FieldAttrPojo.class);
                legend.add(legendAttr.getAttrName());
                legendKeys.add(legendAttr.getAttrName());
            }
            result.put("legend", legend);
            result.put("legendKeys", legendKeys);

            FieldAttrEntity fieldAttrEntity = JSONArray.parseArray(chartEntity.getChartValue().toJSONString(),FieldAttrEntity.class).get(0);
            Integer fieldType = fieldAttrEntity.getFieldType();
            if (chartEntity.getChartType().equals(ChartTypeEnum.PIE_CHART.getCode())) {
                List<ResultDataSeriesChartVO> dataSeries = new ArrayList<>();
                for (String xAxisContrast : xAxis) {
                    String value = "";
                    for (String legendContrast : legend) {
                        ResultDataSeriesChartVO resultDataSeriesChartVO = new ResultDataSeriesChartVO();
                        resultDataSeriesChartVO.setName(xAxisContrast);
                        if (fieldType.equals(FieldTypeEnum.COUNT.getType())) {
                            int ran = random.nextInt(100);
                            value = StringUtil.formatDouble((double) ran, 0, 1);
                        } else {
                            int ran = random.nextInt(1000);
                            value = StringUtil.formatDouble((double) ran, 2, 1);
                        }
                        resultDataSeriesChartVO.setValue(value);
                        dataSeries.add(resultDataSeriesChartVO);
                    }
                }
                result.put("dataSeries",dataSeries);
            } else {
                List<ResultDataChartVO> data = new ArrayList<>();
                for (String legendstr : legend) {
                    JSONArray legendArr = new JSONArray();
                    for (String xAxi : xAxis) {
                        String value;
                        if (fieldType.equals(FieldTypeEnum.COUNT.getType())) {
                            int ran = random.nextInt(100);
                            value = StringUtil.formatDouble((double) ran, 0, 1);
                        } else {
                            int ran = random.nextInt(1000);
                            value = StringUtil.formatDouble((double) ran, 2, 1);
                        }
                        legendArr.add(value);
                    }
                    ResultDataChartVO resultDataChartVO = new ResultDataChartVO(legendstr, null, legendArr);
                    data.add(resultDataChartVO);
                }

                result.put("data",data);
            }
        }

        return result;
    }

    /**
     * 根据 fieldType 给假数据
     * @param fieldAttrPojo
     * @param str
     * @param strKey
     * @author xingxing.xiao
     */
    private void dealwithFieldType(FieldAttrPojo fieldAttrPojo, List<String> str, List<String> strKey) {
        Integer legendAttrFieldType = fieldAttrPojo.getFieldType();
        // 若是下拉框之类的类型，直接用下拉框中的值
        boolean typeFlag = Objects.equals(legendAttrFieldType, FieldTypeEnum.COMBO.getType()) ||
                Objects.equals(legendAttrFieldType, FieldTypeEnum.WORK_ORDER_STAGE.getType());
        if (typeFlag) {
            List<ItemPoJo> items = fieldAttrPojo.getItems();
            for (ItemPoJo item : items) {
                str.add(item.getText());
                strKey.add(item.getValue().toString());
            }
        } else {
            // 查询镜像图表预览参数配置
            CompanyConfigEntity companyConfigEntity = companyConfigModel.getByConfigAlias(CompanyConfigEnum.MIRROR_CHART_PREVIEW.getAlias(), "0");
            String configValue = companyConfigEntity.getConfigValue();
            JSONObject chartConfigObj = JSONObject.parseObject(configValue);

            JSONArray legendAttrValues = chartConfigObj.getJSONArray(legendAttrFieldType.toString());
            if (Objects.nonNull(legendAttrValues)) {
                List<String> strings = JSONObject.parseArray(legendAttrValues.toJSONString(), String.class);
                for (String s : strings) {
                    str.add(s);
                    strKey.add(s);
                }
            } else {
                // 为防止报错，此处用默认值
                JSONArray attrValues = chartConfigObj.getJSONArray("1");
                List<String> strings = JSONObject.parseArray(attrValues.toJSONString(), String.class);
                for (String s : strings) {
                    str.add(s);
                    strKey.add(s);
                }
            }
        }
    }

    /**
     * 处理查询表和统计表展示字段
     * @author xingxing.xiao
     */
    private void displayMirrorAttr(List<FieldAttrPojo> fieldAttrPojoList, JSONArray summaryArray, Map<Long,String> tableLocationMap, JSONArray tableHead, Map<JSONObject,FieldAttrPojo> parseAttrMap) {
        //子表单处理
        List<String> fieldsList = new ArrayList<>();

        if (CollectionsUtil.isNotEmpty(fieldAttrPojoList)) {
            for (FieldAttrPojo fieldAttrPojo : fieldAttrPojoList) {
                String tableLocation = "";
                if (!tableLocationMap.isEmpty()) {
                    tableLocation = tableLocationMap.get(fieldAttrPojo.getFormId()) + "&";
                }
                String attr = fieldAttrPojo.getAttr();
                JSONObject headName = new JSONObject();
                headName.put("name", fieldAttrPojo.getLabelName());
                headName.put("key", tableLocation + attr + "&" + fieldAttrPojo.getFieldType());
                headName.put("formId", fieldAttrPojo.getFormId());
                headName.put("fieldType", fieldAttrPojo.getFieldType());
                headName.put("frozeKey", fieldAttrPojo.getFormId() + attr);
                // 处理子表单字段
                dealWithSubFormAttr(fieldAttrPojo, tableLocation, headName);
                tableHead.add(headName);
                if (!EsUtil.setInData(fieldAttrPojo.getFieldType())) {
                    fieldsList.add(tableLocation + attr);
                }else{
                    fieldsList.add(tableLocation + "data." + attr);
                }
                if(Objects.equals(fieldAttrPojo.getFieldType(), FieldTypeEnum.LINK_BUSINESS_SINGLE.getType()) || Objects.equals(fieldAttrPojo.getFieldType(), FieldTypeEnum.LINK_BUSINESS_MULTI.getType())){
                    String linkAttr = fieldAttrPojo.getLinkedText();
                    if (!EsUtil.setInData(fieldAttrPojo.getFieldType())) {
                        fieldsList.add(tableLocation + linkAttr);
                    }else{
                        fieldsList.add(tableLocation + "data." + linkAttr);
                    }
                }
                parseAttrMap.put(headName, fieldAttrPojo);
            }
        }
        if (summaryArray!=null){
            for(int j=0;j<summaryArray.size();j++){
                JSONObject summaryObject = summaryArray.getJSONObject(j);
                String label = summaryObject.getString("label");
                Long formId = summaryObject.getLong("formId");
                String attr = summaryObject.getString("attr");
                Integer fieldType = summaryObject.getInteger("fieldType");
                String tableLocation = "";
                if(!tableLocationMap.isEmpty()){
                    tableLocation = tableLocationMap.get(formId)+"&";
                }
                JSONObject headName = new JSONObject();
                headName.put("name",label);
                headName.put("key",tableLocation+attr+"&"+fieldType);
                headName.put("formId",formId);
                headName.put("fieldType",fieldType);
                tableHead.add(headName);
            }
        }

    }

    private void dealWithSubFormAttr(FieldAttrPojo fieldAttrPojo, String tableLocation, JSONObject headName) {
        Integer fieldType = fieldAttrPojo.getFieldType();

        //子表单逻辑处理
        if(FieldTypeEnum.isSubFormType(fieldType)){
            SubFormPoJo subFormPoJo = fieldAttrPojo.getSubForm();
            String str = JSONArray.toJSONString(subFormPoJo.getItems(), SerializerFeature.DisableCircularReferenceDetect);
            List<FieldAttrPojo> subFormAttrs = JSONArray.parseArray(str, FieldAttrPojo.class);
            List<JSONObject> subFormItems = new ArrayList<>();
            subFormAttrs.forEach(field -> {
                String attr = field.getAttr();
                JSONObject subFormObj = new JSONObject();
                subFormObj.put("name", fieldAttrPojo.getAttrName() + "-" + field.getAttrName());
                subFormObj.put("key", tableLocation + attr + "&" + field.getFieldType());
                subFormObj.put("formId", fieldAttrPojo.getFormId());
                subFormObj.put("fieldType", field.getFieldType());
                subFormItems.add(subFormObj);
            });
            headName.put("items", subFormItems);
        }
    }

    /**
     * 镜像配置信息
     * @throws XbbException
     * @author xingxing.xiao
     */
    @Override
    public MirrorConfigVO getMirrorInfo(MirrorInstructionDTO mirrorInstructionDTO) throws XbbException {
        MirrorConfigVO mirrorConfigVO = new MirrorConfigVO();
        Long mirrorId = mirrorInstructionDTO.getMirrorId();
        MirrorConfigEntity configEntity = mirrorConfigModel.getByKey(mirrorId);
        if (Objects.isNull(configEntity)) {
            throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403001, MirrorErrorCodeEnum.API_ERROR_403001.getMsg());
        }
        BeanUtil.copyProperties(configEntity, mirrorConfigVO);
        String applicationType = "";
        if (Objects.equals(configEntity.getType(), MirrorTypeEnum.APPLICATION.getType())) {
            // 若是应用，判断下是 工单 or 系统表单 or 自定义应用
            MirrorAppEntity appEntity = mirrorAppModel.getByMirrorId(mirrorId);
            if (Objects.equals(appEntity.getSaasMark(), SaasMarkEnum.SAAS.getCode()) && Objects.equals(appEntity.getAlias(), XbbRefTypeEnum.WORK_ORDER.getAlias())) {
                applicationType = MirrorApplicationTypeEnum.WORK_ORDER.getType();
            } else if (Objects.equals(appEntity.getSaasMark(), SaasMarkEnum.SAAS.getCode())) {
                applicationType = MirrorApplicationTypeEnum.SYSTEM.getType();
            } else {
                applicationType = MirrorApplicationTypeEnum.CUSTOM.getType();
            }
        }
        mirrorConfigVO.setApplicationType(applicationType);
        return mirrorConfigVO;
    }

    /**
     * 镜像应用信息
     * @throws XbbException
     * @author xingxing.xiao
     */
    @Override
    public MirrorInstructionVO getMirrorInstruction(MirrorInstructionDTO mirrorInstructionDTO) throws XbbException {
        MirrorInstructionVO instructionVO = new MirrorInstructionVO();
        Long mirrorId = mirrorInstructionDTO.getMirrorId();
        String corpid = mirrorInstructionDTO.getCorpid();
        try {
            MirrorConfigEntity configEntity = mirrorConfigModel.getByKey(mirrorId);
            if (Objects.isNull(configEntity)) {
                throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403001, MirrorErrorCodeEnum.API_ERROR_403001.getMsg());
            }
            BeanUtil.copyProperties(configEntity, instructionVO);
            String applicationType = "";
            if (Objects.equals(configEntity.getType(), MirrorTypeEnum.APPLICATION.getType())) {
                // 若是应用，判断下是 工单 or 系统表单 or 自定义应用
                MirrorAppEntity appEntity = mirrorAppModel.getByMirrorId(mirrorId);
                if (Objects.equals(appEntity.getSaasMark(), SaasMarkEnum.SAAS.getCode()) && Objects.equals(appEntity.getAlias(), XbbRefTypeEnum.WORK_ORDER.getAlias())) {
                    applicationType = MirrorApplicationTypeEnum.WORK_ORDER.getType();
                } else if (Objects.equals(appEntity.getSaasMark(), SaasMarkEnum.SAAS.getCode())) {
                    applicationType = MirrorApplicationTypeEnum.SYSTEM.getType();
                } else {
                    applicationType = MirrorApplicationTypeEnum.CUSTOM.getType();
                }
            }
            instructionVO.setApplicationType(applicationType);
            instructionVO.setEnable(2);
            // 判断是否本公司创建的镜像
            if (Objects.equals(configEntity.getTargetCorpid(), corpid)) {
                instructionVO.setIsInstall(2);
            } else {
                // 判断镜像应用是否已安装，以及是否可以重新安装镜像
                AppInstallEntity appInstallEntity = appInstallModel.getByMirrorId(mirrorId, corpid);
                instructionVO.setIsInstall(0);
                if (Objects.nonNull(appInstallEntity)) {
                    boolean flag = checkInstall(appInstallEntity);
                    if (!flag) {
                        instructionVO.setIsInstall(0);
                    } else {
                        instructionVO.setIsInstall(1);
                        // 此处是模板中心逻辑，要改造，当有系统表单镜像时应该是不存在开启关闭
                        if (Objects.equals(applicationType, MirrorApplicationTypeEnum.CUSTOM.getType())) {
                            PaasAppEntity paasAppEntity = paasAppModel.getByKey(appInstallEntity.getAppId(), corpid);
                            instructionVO.setEnable(paasAppEntity.getEnable());
                            instructionVO.setAppId(paasAppEntity.getId());
                        }
                    }
                }
            }
        } catch (Exception e) {
            LOG.error("getMirrorInstruction error: ", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        return instructionVO;
    }

    /**
     * 组合镜像简介
     *
     * @param mirrorInstructionAllDTO
     * @return
     * @throws XbbException
     * @author youli.chen
     */
    @Override
    public MirrorInstructionAllVO getMirrorInstructionAll(MirrorInstructionAllDTO mirrorInstructionAllDTO) throws XbbException {
        MirrorInstructionAllVO mirrorInstructionAllVO = new MirrorInstructionAllVO();
        Long appModuleId = mirrorInstructionAllDTO.getAppModuleId();
        String corpid = mirrorInstructionAllDTO.getCorpid();

        AppModuleEntity entity = null;
        if (appModuleId != null) {
            entity = appModuleModel.getByKey(appModuleId);
            if (entity == null) {
                throw new XbbException(TemplateCenterErrorCodeEnum.API_ERROR_402005);
            }
        }

        JSONArray mirrorIdArr;
        if (entity != null) {
            mirrorIdArr = entity.getMirrorId();
        } else {
            mirrorIdArr = mirrorInstructionAllDTO.getMirrorIdArr();
        }
        if(mirrorIdArr == null){
            return mirrorInstructionAllVO;
        }
        List<Long> mirrorIdIn = JSONObject.parseArray(mirrorIdArr.toJSONString(), Long.class);

        try {
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("idIn", mirrorIdIn);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorConfigEntity> mirrorConfigList = mirrorConfigModel.findEntitys(param);
            if (CollectionUtils.isEmpty(mirrorConfigList)) {
                throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403001);
            }

            List<String> targetCorpidList = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            mirrorConfigList.forEach(config -> targetCorpidList.add(config.getTargetCorpid()));

            if(Objects.equals(mirrorConfigList.size(), 1)){
                MirrorConfigEntity configEntity = mirrorConfigList.get(0);
                Long mirrorId = configEntity.getId();

                BeanUtil.copyProperties(configEntity, mirrorInstructionAllVO);
                if (entity != null) {
                    mirrorInstructionAllVO.setAppModuleId(entity.getId());
                }
                String applicationType = "";
                if (Objects.equals(configEntity.getType(), MirrorTypeEnum.APPLICATION.getType())) {
                    // 若是应用，判断下是 工单 or 系统表单 or 自定义应用
                    MirrorAppEntity appEntity = mirrorAppModel.getByMirrorId(mirrorId);
                    if (Objects.equals(appEntity.getSaasMark(), SaasMarkEnum.SAAS.getCode()) && Objects.equals(appEntity.getAlias(), XbbRefTypeEnum.WORK_ORDER.getAlias())) {
                        applicationType = MirrorApplicationTypeEnum.WORK_ORDER.getType();
                    } else if (Objects.equals(appEntity.getSaasMark(), SaasMarkEnum.SAAS.getCode())) {
                        applicationType = MirrorApplicationTypeEnum.SYSTEM.getType();
                    } else {
                        applicationType = MirrorApplicationTypeEnum.CUSTOM.getType();
                    }
                }
                mirrorInstructionAllVO.setApplicationType(applicationType);
                mirrorInstructionAllVO.setEnable(2);
                // 判断是否本公司创建的镜像
                if (Objects.equals(configEntity.getTargetCorpid(), corpid)) {
                    mirrorInstructionAllVO.setIsInstall(2);
                } else {
                    // 判断镜像应用是否已安装，以及是否可以重新安装镜像
                    AppInstallEntity appInstallEntity = appInstallModel.getByMirrorId(mirrorId, corpid);
                    mirrorInstructionAllVO.setIsInstall(0);
                    if (Objects.nonNull(appInstallEntity)) {
                        boolean flag = checkInstall(appInstallEntity);
                        if (!flag) {
                            mirrorInstructionAllVO.setIsInstall(0);
                        } else {
                            mirrorInstructionAllVO.setIsInstall(1);
                            // 此处是模板中心逻辑，要改造，当有系统表单镜像时应该是不存在开启关闭
                            if (Objects.equals(applicationType, MirrorApplicationTypeEnum.CUSTOM.getType())) {
                                PaasAppEntity paasAppEntity = paasAppModel.getByKey(appInstallEntity.getAppId(), corpid);
                                mirrorInstructionAllVO.setEnable(paasAppEntity.getEnable());
                                mirrorInstructionAllVO.setAppId(paasAppEntity.getId());
                            }
                        }
                    }
                }
            } else {
                // 组合镜像
                if (entity != null) {
                    BeanUtil.copyProperties(entity, mirrorInstructionAllVO);
                    mirrorInstructionAllVO.setColor(entity.getBgColor());
                    mirrorInstructionAllVO.setAppModuleId(entity.getId());
                }
                mirrorInstructionAllVO.setType(MirrorTypeEnum.COMPOSE.getType());

                // 组合镜像直接附属于自定义应用，碧云
                mirrorInstructionAllVO.setApplicationType(MirrorApplicationTypeEnum.CUSTOM.getType());
                // 组合镜像类型
                Map<Long, String> mirrorTypeMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                mirrorConfigList.forEach(configEntity -> mirrorTypeMap.put(configEntity.getId(), configEntity.getType()));

                Map<Long, MirrorAppEntity> mirrorAppMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                Map<String, Object> appParam = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                appParam.put("mirrorIdIn", mirrorIdIn);
                appParam.put("del", DelEnum.NORMAL.getDel());
                List<MirrorAppEntity> mirrorAppList = mirrorAppModel.findEntitys(appParam);
                mirrorAppList.forEach(item -> mirrorAppMap.put(item.getMirrorId(), item));

                List<String> applicationTypeList = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                for(Long mirrorId : mirrorIdIn){
                    String type = mirrorTypeMap.get(mirrorId);
                    MirrorAppEntity appEntity = mirrorAppMap.get(mirrorId);
                    String applicationType = "";
                    if (Objects.equals(type, MirrorTypeEnum.APPLICATION.getType())) {
                        // 若是应用，判断下是 工单 or 系统表单 or 自定义应用
                        if (Objects.equals(appEntity.getSaasMark(), SaasMarkEnum.SAAS.getCode()) && Objects.equals(appEntity.getAlias(), XbbRefTypeEnum.WORK_ORDER.getAlias())) {
                            applicationType = MirrorApplicationTypeEnum.WORK_ORDER.getType();
                        } else if (Objects.equals(appEntity.getSaasMark(), SaasMarkEnum.SAAS.getCode())) {
                            applicationType = MirrorApplicationTypeEnum.SYSTEM.getType();
                        } else {
                            applicationType = MirrorApplicationTypeEnum.CUSTOM.getType();
                        }
                    }
                    if(applicationType != ""){
                        applicationTypeList.add(applicationType);
                    }
                }
                // 去重
                List<String> listWithoutDuplicates = applicationTypeList.stream().distinct().collect(Collectors.toList());
                mirrorInstructionAllVO.setApplicationTypeList(listWithoutDuplicates);

                mirrorInstructionAllVO.setEnable(2);
                // 判断是否本公司创建的镜像
                Set target = new HashSet(targetCorpidList);
                if (Objects.equals(target.size(), 1) && target.contains(corpid)) {
                    mirrorInstructionAllVO.setIsInstall(2);
                } else {
                    // 判断镜像应用是否已安装，以及是否可以重新安装镜像
                    HashMap<String, Object> installParam = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    installParam.put("mirrorIdIn", mirrorIdIn);
                    installParam.put("corpid", corpid);
                    installParam.put("del", DelEnum.NORMAL.getDel());
                    List<AppInstallEntity> appInstallEntityList = appInstallModel.findEntitys(installParam);
                    mirrorInstructionAllVO.setIsInstall(0);
                    if(!appInstallEntityList.isEmpty()){
                        List<Long> appInstallIdList = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                        // 检查每一个镜像是否安装
                        boolean flag = true;
                        for(AppInstallEntity appInstallEntity : appInstallEntityList){
                            flag = checkInstall(appInstallEntity);
                            appInstallIdList.add(appInstallEntity.getAppId());
                        }
                        // 只要有一个没有安装，就是未安装
                        if(!flag){
                            mirrorInstructionAllVO.setIsInstall(0);
                        } else {
                            mirrorInstructionAllVO.setIsInstall(1);
                        }
                    }
                }
            }

            if (entity != null) {
                AppChartPojo info = new AppChartPojo();
                info.setVideoUrl(entity.getVideoUrl());
                Long raidersId = entity.getApplicationIdea();
                //RaidersVO raidersVO = new RaidersVO();
                RaidersEntity raidersEntity = new RaidersEntity();
                if (raidersId != null) {
                    raidersEntity = raidersModel.getByKey(raidersId);
                    //BeanUtil.copyProperties(raidersEntity, raidersVO);
                }
                // raidersEntity会有初始化del=0的数据
                info.setApplicationIdea(raidersId == null ? null : raidersEntity);
                info.setApplicationInfo(entity.getApplicationInfo());
                Map<String, Object> marketParam = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                marketParam.put("appId", entity.getId());
                marketParam.put("del", DelEnum.NORMAL.getDel());
                List<MarketDetailEntity> marketList = marketDetailModel.findEntitys(marketParam);
                if (marketList != null) {
                    marketList.stream().sorted(Comparator.comparing(MarketDetailEntity::getSort).reversed()).forEach(market -> {
                        if (StringUtil.isNotEmpty(market.getImage())) {
                            List<String> images = JSONArray.parseArray(market.getImage(), String.class);
                            info.setImages(images);
                        }
                    });
                }
                mirrorInstructionAllVO.setInfo(info);
            }
        } catch (Exception e) {
            LOG.error("getMirrorInstruction error: ", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        return mirrorInstructionAllVO;
    }

    /**
     * 更新镜像配置表中图标简介信息(脚本调用)
     * @author xingxing.xiao
     */
    @Override
    public MirrorUpdateVO updateMirrorInfoScript(MirrorUpdateDTO mirrorUpdateDTO) throws XbbException {
        MirrorUpdateVO mirrorUpdateVO = new MirrorUpdateVO();
        try {
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorConfigEntity> mirrorList = mirrorConfigModel.findEntitys(param);
            Set<Long> mirrorIdIn = new HashSet<>();
            mirrorList.forEach(item -> mirrorIdIn.add(item.getId()));
            param.clear();
            param.put("columns", "id, name, mirror");
            param.put("mirrorIdIn", mirrorIdIn);
            param.put("del", DelEnum.NORMAL.getDel());
            List<RaidersEntity> raidersList = raidersModel.findEntitys(param);
            Map<Long, String> summaryMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            raidersList.forEach(item -> {
                JSONObject mirrorJson = item.getMirror();
                summaryMap.put(mirrorJson.getLong("mirrorId"), mirrorJson.getString("mirrorInstruction"));
            });
            param.clear();
            param.put("mirrorIdIn", mirrorIdIn);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorAppEntity> mirrorAppList = mirrorAppModel.findEntitys(param);
            Map<Long, String> iconMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            Map<Long, String> colorMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            mirrorAppList.forEach(item -> {
                iconMap.put(item.getMirrorId(), item.getIcon());
                colorMap.put(item.getMirrorId(), item.getColor());
            });
            param.clear();
            param.put("mirrorIdIn", mirrorIdIn);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorMenuEntity> menuList = mirrorMenuModel.findEntitys(param);
            Map<Long, JSONArray> targetFormIdsMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            Map<Long, JSONArray> targetFormNamesMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            menuList.forEach(item -> {
                Long mirrorId = item.getMirrorId();
                if (Objects.isNull(targetFormIdsMap.get(mirrorId))){
                    JSONArray targetFormIds = new JSONArray();
                    targetFormIds.add(item.getId());
                    targetFormIdsMap.put(mirrorId, targetFormIds);
                } else {
                    JSONArray targetFormIds = targetFormIdsMap.get(mirrorId);
                    targetFormIds.add(item.getId());
                    targetFormIdsMap.put(mirrorId, targetFormIds);
                }
                if (Objects.isNull(targetFormNamesMap.get(mirrorId))){
                    JSONArray targetFormNames = new JSONArray();
                    targetFormNames.add(item.getName());
                    targetFormNamesMap.put(mirrorId, targetFormNames);
                } else {
                    JSONArray targetFormNames = targetFormNamesMap.get(mirrorId);
                    targetFormNames.add(item.getName());
                    targetFormNamesMap.put(mirrorId, targetFormNames);
                }
            });
            mirrorList.forEach(item -> {
                Long mirrorId = item.getId();
                item.setIcon(iconMap.get(mirrorId));
                item.setColor(colorMap.get(mirrorId));
                item.setSummary(summaryMap.get(mirrorId));
                JSONArray jsonArray = targetFormIdsMap.get(mirrorId);
                item.setTargetFormIds(Objects.nonNull(jsonArray)? jsonArray.toJSONString() : new JSONArray().toJSONString());
                JSONArray nameArray = targetFormNamesMap.get(mirrorId);
                item.setTargetFormNames(Objects.nonNull(nameArray)? nameArray.toJSONString() : new JSONArray().toJSONString());
            });
            Integer integer = mirrorConfigModel.updateBatch(mirrorList);
            mirrorUpdateVO.setCount(integer);
        } catch (Exception e) {
            LOG.error("updateMirrorInfoScript error: ", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        return mirrorUpdateVO;
    }

    /**
     * 回退到上一节点
     * @param mirrorRollBackDTO
     * @return
     * @throws XbbException
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public MirrorRollBackVO rollBackToPrevious(MirrorRollBackDTO mirrorRollBackDTO) throws XbbException {
        String hashKey = mirrorRollBackDTO.getTargetCorpid() +"_"+mirrorRollBackDTO.getAlias();
        MirrorRollBackVO mirrorRollBackVO = new MirrorRollBackVO();
        try {
            String corpid = mirrorRollBackDTO.getTargetCorpid();
            Long appInstallId = mirrorRollBackDTO.getAppInstallId();
            AppInstallEntity appInstallEntity = appInstallModel.getByKey(appInstallId, corpid);
            // 检查系统表单是否有数据
            String json = appInstallEntity.getFormIds();
            List<Long> installFormList = JSONArray.parseArray(json, Long.class);
            newCheckData(mirrorRollBackDTO, installFormList);
            // 获取备份应用id
            PaasAppRetainEntity appRetainEntity = paasAppRetainModel.getByAppInstallId(appInstallEntity.getId(), corpid);
            Long retainAppId = appRetainEntity.getId();
            Long appId = appInstallEntity.getAppId();

            if (Objects.equals(XbbRefTypeEnum.WORK_ORDER.getAlias(),mirrorRollBackDTO.getAlias())){
                //更新工单分类
                Long defaultCategoryId = updateAndDeleteWorkOrderCategory(corpid, appId, retainAppId);
                //更新工单模板
                List<Long> systemTemplateIds = updateAndDeleteWorkOrderTemplate(corpid, appId, retainAppId, defaultCategoryId);
                //更新工单阶段tb_work_order_stage
                updateAndDeleteWorkOrderStage(corpid, appId, retainAppId, systemTemplateIds);
                //更新工单表单
                List<Long> formIds = updateAndDeleteWorkOrderForm(corpid, appId, retainAppId, systemTemplateIds);
                //更新工单解释
                updateAndDeleteWorkOrderFormExplain(corpid, appId, retainAppId, formIds);
                // 更新工单打印模板
                updateAndDeleteWorkOrderPrintTemplate(corpid, appId, retainAppId, formIds);
                //更新标签分组
                updateAndDeleteLabelGroup(corpid, appId, retainAppId, formIds);
                //更新标签
                udpateAndDeleteLabel(corpid, appId, retainAppId, formIds);
                //更新低代码
                udpateAndDeleteLowCode(corpid, appId, retainAppId, formIds, XbbRefTypeEnum.WORK_ORDER.getCode());
            } else {
                //注：该map可删除，form的覆盖时一对一的
                Map<Long, Long> sourceFormId2NowFormIdMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);

                //更新menu
                List<Long> systemMenuIdIn = updateAndDeleteMenu(corpid, appId, retainAppId);
                //更新form
                List<Long> systemFormIdIn = updateAndDeleteForm(corpid, appId, retainAppId, systemMenuIdIn, sourceFormId2NowFormIdMap);
                // 更新或删除业务规则
                updateAndDeleteBusinessRule(corpid, appId, retainAppId, systemFormIdIn);
                //更新form explain
                updateAndDeleteFormExplain(corpid, appId, retainAppId, systemFormIdIn);
                //更新 Stage
                StageRollbackPojo stageRollbackPojo = new StageRollbackPojo(corpid, retainAppId, systemFormIdIn, sourceFormId2NowFormIdMap);
                updateAndDeleteStageThruster(stageRollbackPojo);
                //更新打印模板
                updateAndDeletePrintTemplate(corpid, appId, retainAppId, systemFormIdIn);
                //更新标签分组
                updateAndDeleteLabelGroup(corpid, appId, retainAppId, systemFormIdIn);
                //更新标签
                udpateAndDeleteLabel(corpid, appId, retainAppId, systemFormIdIn);
                // 更新低代码
                udpateAndDeleteLowCode(corpid, appId, retainAppId, systemFormIdIn);
            }
            //更新应用回退到上一版本的值为MirrorRollBackTypeEnum.ROLL_BACK_TO_PREVIOUS
            appInstallEntity.setIsToPrevious(MirrorRollBackTypeEnum.ROLL_BACK_TO_PREVIOUS.getFlag());
            appInstallModel.update(appInstallEntity);
            formInstallModel.deleteByAppInstallId(corpid,appInstallEntity.getId());
            //添加一天一个应用一加公司只允许回退2次
            String value = paasRedisHelper.getValue(RedisPrefixConstant.MIRROR_ROLLBACK, hashKey);
            if (Objects.nonNull(value) && !Objects.equals(Integer.valueOf(value),BasicConstant.ZERO)){
                Integer integer = Integer.valueOf(value);
                integer--;
                paasRedisHelper.setValue(RedisPrefixConstant.MIRROR_ROLLBACK,hashKey,integer, RedisConstant.LONG_DURATION);
            }else {
                paasRedisHelper.setValue(RedisPrefixConstant.MIRROR_ROLLBACK,hashKey,2, RedisConstant.LONG_DURATION);
            }
            mirrorRollBackVO.setMirrorId(mirrorRollBackDTO.getMirrorId());
        } catch (XbbException e){
            throw e;
        } catch (Exception e){
            LOG.error("回退到上一版本出错", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        return mirrorRollBackVO;
    }

    /**
     * 回退低代码-非工单业务调用
     * @param corpid
     * @param appId
     * @param retainAppId
     * @param systemFormIdIn
     * @throws XbbException
     */
    private void udpateAndDeleteLowCode(String corpid, Long appId, Long retainAppId, List<Long> systemFormIdIn) throws XbbException {
        udpateAndDeleteLowCode(corpid, appId, retainAppId, systemFormIdIn, null);
    }

    /**
     * 回退低代码
     * @param corpid
     * @param appId
     * @param retainAppId
     * @param systemFormIdIn
     * @throws XbbException
     */
    private void udpateAndDeleteLowCode(String corpid, Long appId, Long retainAppId, List<Long> systemFormIdIn, Integer businessType) throws XbbException {

        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("retainAppId", retainAppId);
        param.put("corpid", corpid);
        param.put("sourceFrom", BasicConstant.TWO);
        param.put("del", DelEnum.NORMAL.getDel());
        if (Objects.nonNull(businessType)) {
            param.put("sourceBusinessType", businessType);
        }
        List<LowCodeRetaionEntity> lowCodeRetaionEntityList = lowCodeRetaionModel.findEntities(param);
        if (CollectionsUtil.isEmpty(lowCodeRetaionEntityList)) {
            // 没有备份时，说明是初始状态安装，还原至初始状态
            lowCodeHelp.deleteLowCode(corpid, systemFormIdIn, businessType);
            return;
        }
        Map<Long, LowCodeRetaionEntity> lowCodeRetaionEntityMap = lowCodeRetaionEntityList.stream().collect(Collectors.toMap(LowCodeRetaionEntity::getSourceId, (t -> t), (t, t1) -> t1));

        param.clear();
        param.put("corpid", corpid);
        param.put("formIdIn", systemFormIdIn);
        if (Objects.nonNull(businessType)) {
            param.put(StringConstant.BUSINESS_TYPE, businessType);
        }
        List<LowCodeJsFileEntity> lowCodeJsFileEntityList = lowCodeJsFileModel.findEntitys(param);
        lowCodeJsFileEntityList.forEach(item -> {
            LowCodeRetaionEntity lowCodeRetaionEntity = lowCodeRetaionEntityMap.get(item.getId());
            if (Objects.nonNull(lowCodeRetaionEntity)) {
                item.setUpdateTime(lowCodeRetaionEntity.getUpdateTime());
                item.setFormId(lowCodeRetaionEntity.getSourceFormId());
                item.setSaasMark(lowCodeRetaionEntity.getSourceSaasMark());
                item.setBusinessType(lowCodeRetaionEntity.getSourceBusinessType());
                item.setWhetherToPublish(lowCodeRetaionEntity.getWhetherToPublish());
                item.setCreatorId(lowCodeRetaionEntity.getCreatorId());
                item.setDel(lowCodeRetaionEntity.getDel());

                //回退低代码
                String publish = lowCodeHelp.copyLowCodeRepFile(lowCodeRetaionEntity.getPublish(), StringConstant.LOW_CODE_RELEASE_SUFFIX, corpid, appId, lowCodeRetaionEntity.getSourceFormId());
                item.setPublish(publish);
                String dev = lowCodeHelp.copyLowCodeRepFile(lowCodeRetaionEntity.getPublish(), StringConstant.LOW_CODE_DEV_SUFFIX, corpid, appId, lowCodeRetaionEntity.getSourceFormId());
                item.setUnreleased(dev);
            } else {
                item.setDel(DelEnum.DELETE.getDel());
            }
        });
        if (CollectionsUtil.isNotEmpty(lowCodeJsFileEntityList)){
            lowCodeJsFileModel.updateBatch(lowCodeJsFileEntityList, corpid);
        }

        // 回退低代码配置
        backLowCodeConfig(corpid, systemFormIdIn, lowCodeRetaionEntityMap, businessType);
    }

    /**
     * 回退低代码配置
     * @param corpid
     * @param lowCodeRetaionEntityMap
     */
    private void backLowCodeConfig(String corpid, Collection<Long> formIdIn, Map<Long, LowCodeRetaionEntity> lowCodeRetaionEntityMap, Integer businessType) {

        List<CustomButtonPojo> allCustomButtonPojoList = new ArrayList<>();
        List<CustomButtonActionPojo> allCustomButtonActionPojoList = new ArrayList<>();
        List<LowCodeActionSettingPojo> allActionSettingList = new ArrayList<>();
        for (Long formId : lowCodeRetaionEntityMap.keySet()) {
            LowCodeRetaionEntity lowCodeRetaionEntity = lowCodeRetaionEntityMap.get(formId);
            String lowCodeConfig = lowCodeRetaionEntity.getLowCodeConfig();
            LowCodeConfigRetaionPojo lowCodeConfigRetaionPojo = JSONObject.parseObject(lowCodeConfig, LowCodeConfigRetaionPojo.class);
            List<CustomButtonPojo> customButtonPojoList = lowCodeConfigRetaionPojo.getCustomButtonPojoList();
            if (CollectionsUtil.isNotEmpty(customButtonPojoList)) {
                allCustomButtonPojoList.addAll(customButtonPojoList);
            }
            List<LowCodeActionSettingPojo> actionSettingList = lowCodeConfigRetaionPojo.getActionSettingList();
            if (CollectionsUtil.isNotEmpty(actionSettingList)) {
                allActionSettingList.addAll(actionSettingList);
            }
        }

        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("corpid", corpid);
        param.put("formIdIn", formIdIn);
        if (Objects.nonNull(businessType)) {
            param.put(StringConstant.BUSINESS_TYPE, businessType);
        }
        List<CustomButtonEntity> customButtonEntityList = customButtonModel.findEntitys(param);
        List<CustomButtonActionEntity> customButtonActionEntityList = customButtonActionModel.findEntitys(param);
        List<LowCodeActionSettingEntity> lowCodeActionSettingEntityList = lowCodeActionSettingModel.findEntitys(param);

        // 低代码自定义按钮
        Map<Long, CustomButtonPojo> buttonPojoMap = allCustomButtonPojoList.stream().collect(Collectors.toMap(CustomButtonPojo::getId, t -> t, (t, t1) -> t1));
        customButtonEntityList.forEach(item -> {
            CustomButtonPojo customButtonPojo = buttonPojoMap.get(item.getId());
            if (Objects.nonNull(customButtonPojo)) {
                item.setFormId(customButtonPojo.getFormId());
                item.setName(customButtonPojo.getName());
                item.setIcon(customButtonPojo.getIcon());
                item.setColor(customButtonPojo.getColor());
                item.setType(customButtonPojo.getType());
                item.setSort(customButtonPojo.getSort());
                item.setDel(DelEnum.NORMAL.getDel());
            } else {
                item.setDel(DelEnum.DELETE.getDel());
            }
        });

        // 按钮动作设置
        allCustomButtonPojoList.forEach(item-> item.getActionList().forEach(action -> {
            action.setButtonId(item.getId());
            allCustomButtonActionPojoList.add(action);
        }));
        Map<Long, CustomButtonActionPojo> customButtonActionPojoMap = allCustomButtonActionPojoList.stream().collect(Collectors.toMap(CustomButtonActionPojo::getId, t -> t, (t, t1) -> t1));
        customButtonActionEntityList.forEach(item -> {
            CustomButtonActionPojo customButtonActionPojo = customButtonActionPojoMap.get(item.getId());
            if (Objects.nonNull(customButtonActionPojo)) {
                item.setFormId(customButtonActionPojo.getFormId());
                item.setButtonId(customButtonActionPojo.getButtonId());
                item.setActionType(customButtonActionPojo.getActionType());
                String customButtonActionPojoParam = Objects.isNull(customButtonActionPojo.getParam()) ? null : customButtonActionPojo.getParam().toJSONString();
                item.setParam(customButtonActionPojoParam);
                item.setSort(customButtonActionPojo.getSort());
                item.setDel(DelEnum.NORMAL.getDel());
            } else {
                item.setDel(DelEnum.DELETE.getDel());
            }
        });

        // 低代码动作设置
        Map<Long, LowCodeActionSettingPojo> lowCodeActionSettingPojoMap = allActionSettingList.stream().collect(Collectors.toMap(LowCodeActionSettingPojo::getId, t -> t, (t, t1) -> t1));
        lowCodeActionSettingEntityList.forEach(item -> {
            LowCodeActionSettingPojo lowCodeActionSettingPojo = lowCodeActionSettingPojoMap.get(item.getId());
            if (Objects.nonNull(lowCodeActionSettingPojo)) {
                item.setType(lowCodeActionSettingPojo.getType());
                item.setTriggerTiming(lowCodeActionSettingPojo.getTriggerTiming());
                item.setActionType(lowCodeActionSettingPojo.getActionType());
                item.setAttr(lowCodeActionSettingPojo.getAttr());
                item.setFieldType(lowCodeActionSettingPojo.getFieldType());
                String lowCodeActionSettingPojoParam = Objects.isNull(lowCodeActionSettingPojo.getParam()) ? null : lowCodeActionSettingPojo.getParam().toJSONString();
                item.setParam(lowCodeActionSettingPojoParam);
                item.setSort(lowCodeActionSettingPojo.getSort());
                item.setDel(DelEnum.NORMAL.getDel());
            } else {
                item.setDel(DelEnum.DELETE.getDel());
            }
        });

        //入库
        if (CollectionsUtil.isNotEmpty(customButtonEntityList)) {
            customButtonModel.updateBatch(customButtonEntityList, corpid);
        }
        if (CollectionsUtil.isNotEmpty(customButtonActionEntityList)) {
            customButtonActionModel.updateBatch(customButtonActionEntityList, corpid);
        }
        if (CollectionsUtil.isNotEmpty(lowCodeActionSettingEntityList)) {
            lowCodeActionSettingModel.updateBatch(lowCodeActionSettingEntityList, corpid);
        }
    }

    private void udpateAndDeleteLabel(String corpid, Long appId, Long retainAppId, List<Long> systemFormIdIn) throws XbbException {
        try {
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("retainAppId", retainAppId);
            param.put("corpid", corpid);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorRetainLabelEntity> mirrorRetainLabelEntities = mirrorRetainLabelModel.findEntitys(param);
            Map<Long, MirrorRetainLabelEntity> retainGroupMap = new HashMap<>(mirrorRetainLabelEntities.size());
            mirrorRetainLabelEntities.forEach(item->retainGroupMap.put(item.getSourceGroupId(), item));
            param.clear();
            param.put("appId", appId);
            param.put("corpid", corpid);
            param.put("formIdIn", systemFormIdIn);
            List<LabelEntity> entitys = labelModel.findEntitys(param);
            entitys.forEach(item->{
                MirrorRetainLabelEntity mirrorRetainLabelEntity = retainGroupMap.get(item.getGroupId());
                if (Objects.nonNull(mirrorRetainLabelEntity)){
                    item.setUpdateTime(item.getUpdateTime());
                    item.setAppId(mirrorRetainLabelEntity.getSourceAppId());
                    item.setName(mirrorRetainLabelEntity.getName());
                    item.setColor(mirrorRetainLabelEntity.getColor());
                    item.setEnable(mirrorRetainLabelEntity.getEnable());
                    item.setGroupId(mirrorRetainLabelEntity.getSourceGroupId());
                    item.setFormId(mirrorRetainLabelEntity.getSourceFormId());
                    item.setSort(mirrorRetainLabelEntity.getSort());
                    item.setCreatorId(mirrorRetainLabelEntity.getCreatorId());
                    item.setDel(mirrorRetainLabelEntity.getDel());
                } else {
                    item.setDel(DelEnum.DELETE.getDel());
                }
            });
            if (entitys.size()>0){
                labelModel.updateBatch(entitys, corpid);
            }
        } catch (Exception e) {
            LOG.error("标签回滚失败", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }


    }

    /**
     * 更新标签处理
     * @param corpid
     * @param appId
     * @param retainAppId
     * @param systemFormIdIn
     */
    private void updateAndDeleteLabelGroup(String corpid, Long appId, Long retainAppId, List<Long> systemFormIdIn) throws XbbException {
        try {
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("retainAppId", retainAppId);
            param.put("corpid", corpid);
            param.put("del", DelEnum.NORMAL.getDel());
            List<MirrorRetainLabelGroupEntity> mirrorRetainLabelGroupEntities = mirrorRetainLabelGroupModel.findEntitys(param);
            Map<Long, MirrorRetainLabelGroupEntity> retainGroupMap = new HashMap<>(mirrorRetainLabelGroupEntities.size());
            mirrorRetainLabelGroupEntities.forEach(item->retainGroupMap.put(item.getSourceGroupId(), item));
            param.clear();
            param.put("appId", appId);
            param.put("corpid", corpid);
            param.put("formIdIn", systemFormIdIn);
            List<LabelGroupEntity> entitys = labelGroupModel.findEntitys(param);
            entitys.forEach(item->{
                MirrorRetainLabelGroupEntity mirrorRetainLabelGroupEntity = retainGroupMap.get(item.getId());
                if (Objects.nonNull(mirrorRetainLabelGroupEntity)){
                    item.setUpdateTime(item.getUpdateTime());
                    item.setAppId(mirrorRetainLabelGroupEntity.getSourceAppId());
                    item.setGroupName(mirrorRetainLabelGroupEntity.getGroupName());
                    item.setFormId(mirrorRetainLabelGroupEntity.getSourceFormId());
                    item.setSort(mirrorRetainLabelGroupEntity.getSort());
                    item.setCreatorId(mirrorRetainLabelGroupEntity.getCreatorId());
                    item.setDel(mirrorRetainLabelGroupEntity.getDel());
                } else {
                    item.setDel(DelEnum.DELETE.getDel());
                }
            });
            if (entitys.size()>0){
                labelGroupModel.updateBatch(entitys, corpid);
            }
        } catch (Exception e) {
            LOG.error("标签分组回滚失败", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
    }

    /**
     * 回退到初始化工单打印模板
     * @param corpid
     * @param appId
     * @param retainAppId
     * @param formIds
     */
    private void updateAndDeleteWorkOrderPrintTemplate(String corpid, Long appId, Long retainAppId, List<Long> formIds) throws XbbException{
        try {
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("retainAppId", retainAppId);
            param.put("corpid", corpid);
            param.put("businessType", XbbRefTypeEnum.WORK_ORDER.getCode());
            param.put("del", DelEnum.NORMAL.getDel());
            List<PaasPrintTemplateRetainEntity> entitys = paasPrintTemplateRetainModel.list(param);
            Map<Long, PaasPrintTemplateRetainEntity> retainPrintTemplateMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            entitys.forEach(item -> retainPrintTemplateMap.put(item.getSourcePrintTemplateId(), item));
            param.clear();
            param.put("appId", appId);
            param.put("corpid", corpid);
            param.put("formIdIn", formIds);
            List<PaasPrintTemplateEntity> paasPrintTemplateEntityList = paasPrintTemplateDao.findEntitys(param);
            paasPrintTemplateEntityList.forEach(item -> {
                PaasPrintTemplateRetainEntity paasPrintTemplateRetainEntity = retainPrintTemplateMap.get(item.getId());
                if (Objects.nonNull(paasPrintTemplateRetainEntity)) {
                    item.setUpdateTime(paasPrintTemplateRetainEntity.getUpdateTime());
                    item.setCreatorId(paasPrintTemplateRetainEntity.getCreatorId());
                    item.setConfigInfo(paasPrintTemplateRetainEntity.getConfigInfo());
                    item.setHtml(paasPrintTemplateRetainEntity.getHtml());
                    item.setName(paasPrintTemplateRetainEntity.getName());
                    item.setDel(paasPrintTemplateRetainEntity.getDel());
                    item.setEnable(paasPrintTemplateRetainEntity.getEnable());
                } else {
                    item.setDel(DelEnum.DELETE.getDel());
                }
            });
            if (!paasPrintTemplateEntityList.isEmpty()) {
                paasPrintTemplateDao.updateBatch(paasPrintTemplateEntityList, corpid);
            }
        }  catch (Exception e){
            LOG.error("覆盖打印模板出错", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
    }

    /**
     * 镜像回退上一版本——更新或删除业务规则
     * @param corpid
     * @param appId
     * @param retainAppId
     * @param systemFormIdIn
     * @author xingxing.xiao
     * @date 2020/06/15 11:02
     */
    private void updateAndDeleteBusinessRule(String corpid, Long appId, Long retainAppId, List<Long> systemFormIdIn) throws XbbException {
        try {
            HashMap<String,Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("corpid", corpid);
            param.put("retainAppId", retainAppId);
            param.put("del",DelEnum.NORMAL.getDel());
            List<BusinessRuleInfoRetainEntity> ruleInfoRetainEntities = businessRuleInfoRetainModel.findEntitys(param);
            Map<Long, BusinessRuleInfoRetainEntity> retainRuleInfoMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            ruleInfoRetainEntities.forEach(item -> retainRuleInfoMap.put(item.getSourceBusinessRuleId(), item));

            param.clear();
            param.put("appId", appId);
            param.put("corpid",corpid);
            param.put("formIdIn", systemFormIdIn);
            List<BusinessRuleInfoEntity> ruleInfoEntities = businessRuleInfoModel.findEntitys(param);
            ruleInfoEntities.forEach(item -> {
                BusinessRuleInfoRetainEntity retainEntity = retainRuleInfoMap.get(item.getId());
                if (Objects.nonNull(retainEntity)) {
                    item.setActiveName(retainEntity.getActiveName());
                    item.setActiveType(retainEntity.getActiveType());
                    item.setActiveSaasMark(retainEntity.getActiveSaasMark());
                    item.setActiveBusinessType(retainEntity.getActiveBusinessType());
                    item.setActiveFormId(retainEntity.getActiveFormId());
                    item.setActiveMenuId(retainEntity.getActiveMenuId());
                    item.setActiveAppId(retainEntity.getActiveAppId());
                    item.setActiveOperationType(retainEntity.getActiveOperationType());
                    item.setActiveFilterConditions(retainEntity.getActiveFilterConditions());
                    item.setActiveOperations(retainEntity.getActiveOperations());
                    item.setActiveMemo(retainEntity.getActiveMemo());
                    item.setActiveEnable(retainEntity.getActiveEnable());
                    item.setInactiveName(retainEntity.getInactiveName());
                    item.setInactiveType(retainEntity.getInactiveType());
                    item.setInactiveSaasMark(retainEntity.getInactiveSaasMark());
                    item.setInactiveBusinessType(retainEntity.getInactiveBusinessType());
                    item.setInactiveFormId(retainEntity.getInactiveFormId());
                    item.setInactiveMenuId(retainEntity.getInactiveMenuId());
                    item.setInactiveAppId(retainEntity.getInactiveAppId());
                    item.setInactiveOperationType(retainEntity.getInactiveOperationType());
                    item.setInactiveFilterConditions(retainEntity.getInactiveFilterConditions());
                    item.setInactiveOperations(retainEntity.getInactiveOperations());
                    item.setInactiveMemo(retainEntity.getInactiveMemo());
                    item.setInactiveEnable(retainEntity.getInactiveEnable());
                    item.setPriority(retainEntity.getPriority());
                    item.setCreatorId(retainEntity.getCreatorId());
                    item.setAddTime(retainEntity.getAddTime());
                    item.setUpdateTime(retainEntity.getUpdateTime());
                    item.setDel(retainEntity.getDel());
                } else {
                    item.setDel(DelEnum.DELETE.getDel());
                }
            });
            if (!ruleInfoEntities.isEmpty()) {
                businessRuleInfoModel.updateBatch(ruleInfoEntities, corpid);
            }
        } catch (Exception e) {
            LOG.error("业务规则更新出错", e.getMessage());
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
    }

    @Override
    public MirrorRollBackVO checkData(MirrorRollBackDTO mirrorRollBackDTO) throws  XbbException{
        MirrorRollBackVO mirrorRollBackVO = new MirrorRollBackVO();
        String corpid = mirrorRollBackDTO.getTargetCorpid();
        Long appInstallId = mirrorRollBackDTO.getAppInstallId();
        AppInstallEntity appInstallEntity = appInstallModel.getByKey(appInstallId, corpid);
        // 检查系统表单是否有数据
        String json = appInstallEntity.getFormIds();
        List<Long> installFormList = JSONArray.parseArray(json, Long.class);
        newCheckData(mirrorRollBackDTO, installFormList);
        return mirrorRollBackVO;
    }

    /**
     * 获取镜像配置
     *
     * @return MirrorConfigListVo
     * @throws XbbException
     */
    @Override
    public MirrorConfigListVo getMirrorConfigList() throws XbbException {
        LOG.info("========================pro-web-mirror getMirrorConfigList========================");
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("publishModuleCenter", 1);
        param.put("del", DelEnum.NORMAL.getDel());
        List<MirrorConfigEntity> mirrorConfigList = mirrorConfigModel.findEntitys(param);
        List<Long> mirrorIdIn = new ArrayList<>();
        for(MirrorConfigEntity mirrorConfigEntity : mirrorConfigList) {
            if (Objects.equals(mirrorConfigEntity.getType(), MirrorTypeEnum.APPLICATION.getType())) {
                mirrorIdIn.add(mirrorConfigEntity.getId());
            }
        }
        param.clear();
        param.put("mirrorIdIn", mirrorIdIn);
        param.put("del", DelEnum.NORMAL.getDel());
        List<MirrorAppEntity> mirrorAppEntityList = mirrorAppModel.findEntitys(param);
        Map<Long, MirrorAppEntity> mirrorAppMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        mirrorAppEntityList.forEach(mirrorApp -> mirrorAppMap.put(mirrorApp.getMirrorId(), mirrorApp));
        List<MirrorConfigListPojo> resultMirrorConfigList = new ArrayList<>();
        mirrorConfigList.forEach(mirrorConfig -> {
            MirrorConfigListPojo mirrorConfigListPojo = new MirrorConfigListPojo(mirrorConfig.getId(), mirrorConfig.getName(), mirrorConfig.getType(),
                    mirrorConfig.getCreateSource(), mirrorConfig.getLabelId(), mirrorConfig.getTargetCorpid(), mirrorConfig.getTargetCorpName(),
                    mirrorConfig.getTargetAppId(), mirrorConfig.getTargetAppName(), mirrorConfig.getPublishRaiders(), mirrorConfig.getPublishModuleCenter(),
                    mirrorConfig.getCreatorId(), mirrorConfig.getAddTime(), mirrorConfig.getUpdateTime(), mirrorConfig.getDel(), mirrorConfig.getIcon(),
                    mirrorConfig.getColor(), mirrorConfig.getSummary(), mirrorConfig.getTargetFormIds(), mirrorConfig.getTargetFormNames(), mirrorConfig.getSaasMark());
            MirrorAppEntity mirrorAppEntity = mirrorAppMap.get(mirrorConfig.getId());
            if (mirrorAppEntity != null) {
                mirrorConfig.setSaasMark(mirrorAppEntity.getSaasMark());
                mirrorConfigListPojo.setSaasMark(mirrorAppEntity.getSaasMark());
            }
            resultMirrorConfigList.add(mirrorConfigListPojo);
        });
        MirrorConfigListVo mirrorConfigListVo = new MirrorConfigListVo();
        mirrorConfigListVo.setMirrorConfigList(resultMirrorConfigList);
        return mirrorConfigListVo;
    }

    /**
     * 检查系统表单是否有数据
     * @param mirrorRollBackDTO
     * @param installFormList
     * @throws XbbException
     * @author guohui.zhang
     */
    private void newCheckData(MirrorRollBackDTO mirrorRollBackDTO, List<Long> installFormList) throws XbbException{
        String targetCorpid = mirrorRollBackDTO.getTargetCorpid();
        HashMap<String,Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("appId", mirrorRollBackDTO.getAppId());
        param.put("corpid", targetCorpid);
        param.put("idIn", installFormList);
        List<PaasFormEntity> paasFormModelEntitys = paasFormModel.findEntity(param);
        List<Long> systemFormList = new ArrayList<>();
        paasFormModelEntitys.forEach(item -> {
            if (Objects.equals(item.getSaasMark(), SaasMarkEnum.SAAS.getCode())) {
                systemFormList.add(item.getId());
            }
        });

        if (Objects.equals(mirrorRollBackDTO.getAlias(),XbbRefTypeEnum.WORK_ORDER.getAlias())){
            checkWorkOrderData(mirrorRollBackDTO.getTargetCorpid(),mirrorRollBackDTO.getAppId(),new HashSet<>(installFormList));
        }else {
            param.put("formIdIn", systemFormList);
            param.put("del", DelEnum.NORMAL.getDel());
            param.put("corpid", targetCorpid);
            List<PaasFormEntity> paasFormList = paasFormModel.findEntity(param);
            checkFormData(paasFormList, targetCorpid, mirrorRollBackDTO.getAppId());

            checkThrusterData(paasFormList, targetCorpid);
        }
    }

    /**
     * 检查阶段推进器相关表是否有数据
     * @param formList
     * @param targetCorpid
     * @throws XbbException
     */
    private void checkThrusterData(List<PaasFormEntity> formList, String targetCorpid) throws XbbException {
        List<Long> allFormIdIn = formList.stream()
                .filter(formEntity ->
//                        Objects.equals(formEntity.getSaasMark(), SaasMarkEnum.PAAS.getCode()) ||
                        Objects.equals(formEntity.getBusinessType(), XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode()) ||
                        Objects.equals(formEntity.getBusinessType(), XbbRefTypeEnum.SALES_OPPORTUNITY.getCode()) ||
                        Objects.equals(formEntity.getBusinessType(), XbbRefTypeEnum.CLUE.getCode()))
                .map(PaasFormEntity::getId).collect(Collectors.toCollection(() -> new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY)));

        checkStageLog(formList, targetCorpid);

        checkStageWorkLog(allFormIdIn, targetCorpid);

        // sale_stage_modify_log
        Map<String, Object> params = new HashMap<>();
        params.put("corpid", targetCorpid);
        params.put("formIdIn", allFormIdIn);
        params.put("del", DelEnum.NORMAL.getDel());
        List<SaleStageModifyLogEntity> saleStageModifyLogEntities = saleStageModifyLogModel.findEntitys(params);
        if (saleStageModifyLogEntities.size() > 0) {
            List<Long> saleStageModifyLogIdList = new ArrayList<>();
            saleStageModifyLogEntities.forEach(item->{
                saleStageModifyLogIdList.add(item.getId());
            });
            String str = "需要删除的sale_stage_modify_log表 Id：" + JSON.toJSONString(saleStageModifyLogIdList);
            LOG.error("镜像阶段推进器-" + str);
            throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403051, MirrorErrorCodeEnum.API_ERROR_403051.getMsg(), I18nMessageUtil.getMessage(MessageConstant.SALE_STAGE_MODIFY_LOG), str);
        }

        // tb_sale_stage_sync_status
        params.clear();
        params.put("corpid", targetCorpid);
        params.put("formIdIn", allFormIdIn);
        params.put("del", DelEnum.NORMAL.getDel());
        List<SaleStageSyncStatusEntity> saleStageSyncStatusEntities = saleStageSyncStatusModel.findEntitys(params);
        if (saleStageSyncStatusEntities.size() > 0) {
            List<Long> saleStageSyncStatusId = new ArrayList<>();
            saleStageSyncStatusEntities.forEach(item->{
                saleStageSyncStatusId.add(item.getId());
            });
            String str = "需要删除的 tb_sale_stage_sync_status表 Id：" + JSON.toJSONString(saleStageSyncStatusId);
            LOG.error("镜像阶段推进器-" + str);
            throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403051, MirrorErrorCodeEnum.API_ERROR_403051.getMsg(), I18nMessageUtil.getMessage(MessageConstant.SALE_STAGE_SYNC_STATUS), str);
        }

        // timeout_remind
        params.clear();
        params.put("corpid", targetCorpid);
        params.put("formIdIn", allFormIdIn);
        params.put("del", DelEnum.NORMAL.getDel());
        List<StageTimeoutRemindEntity> stageTimeoutRemindEntities = stageTimeoutRemindModel.findEntitys(params);
        if (stageTimeoutRemindEntities.size() > 0) {
            List<Long> stageTimeoutRemindIdList = new ArrayList<>();
            stageTimeoutRemindEntities.forEach(item->{
                stageTimeoutRemindIdList.add(item.getId());
            });
            String str = "需要删除的阶段超时提醒表 数据Id：" + JSON.toJSONString(stageTimeoutRemindIdList);
            LOG.error("镜像阶段推进器-" + str);
            throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403051, MirrorErrorCodeEnum.API_ERROR_403051.getMsg(), I18nMessageUtil.getMessage(MessageConstant.STAGE_TIMEOUT_REMIND), str);
        }

    }

    /**
     * 检查阶段任务日志是否有数据
     * @param allFormIdIn
     * @param targetCorpid
     * @throws XbbException
     */
    private void checkStageWorkLog(List<Long> allFormIdIn, String targetCorpid) throws XbbException {

        Map<String, Object> params = new HashMap<>();
        params.put("corpid", targetCorpid);
        params.put("formIdIn", allFormIdIn);
        params.put("del", 0);
        List<StageWorkEntity> stageWorkEntities = stageWorkModel.findEntitys(params);

        List<Long> paasStageIdIn = new ArrayList<>();
        List<Long> customerStageIdIn = new ArrayList<>();
        List<Long> opportunityStageIdIn = new ArrayList<>();
        List<Long> clueStageIdIn = new ArrayList<>();
        stageWorkEntities.forEach(item -> {
            if (Objects.equals(SaasMarkEnum.PAAS.getCode(), item.getSaasMark())) {
                paasStageIdIn.add(item.getStageId());
            } else if (Objects.equals(XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode(), item.getBusinessType())) {
                customerStageIdIn.add(item.getStageId());
            } else if (Objects.equals(XbbRefTypeEnum.SALES_OPPORTUNITY.getCode(), item.getBusinessType())) {
                opportunityStageIdIn.add(item.getStageId());
            } else if (Objects.equals(XbbRefTypeEnum.CLUE.getCode(), item.getBusinessType())) {
                clueStageIdIn.add(item.getStageId());
            }
        });

//        checkSingleStageWorkLog(targetCorpid, paasStageIdIn, SaasMarkEnum.PAAS.getCode(), XbbRefTypeEnum.PAAS.getCode());
        checkSingleStageWorkLog(targetCorpid, customerStageIdIn, SaasMarkEnum.SAAS.getCode(), XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode(), XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getName());
        checkSingleStageWorkLog(targetCorpid, opportunityStageIdIn, SaasMarkEnum.SAAS.getCode(), XbbRefTypeEnum.SALES_OPPORTUNITY.getCode(), XbbRefTypeEnum.SALES_OPPORTUNITY.getName());
        checkSingleStageWorkLog(targetCorpid, clueStageIdIn, SaasMarkEnum.SAAS.getCode(), XbbRefTypeEnum.CLUE.getCode(), XbbRefTypeEnum.CLUE.getName());

    }

    /**
     * 检查 单业务类型阶段任务日志是否有数据
     * @param targetCorpid
     * @param stageIdIn
     * @param businessType
     * @throws XbbException
     */
    private void checkSingleStageWorkLog(String targetCorpid, List<Long> stageIdIn, Integer saasMark, Integer businessType, String businessName) throws XbbException {
        if (stageIdIn.isEmpty()) {
            return;
        }
        Map<String, Object> params = new HashMap<>();
        params.put("corpid", targetCorpid);
        params.put("stageIdIn", stageIdIn);
        params.put("del", DelEnum.NORMAL.getDel());
        List entities = null;
        if (Objects.equals(SaasMarkEnum.PAAS.getCode(), saasMark)) {
            entities = stageWorkLogModel.findEntitys(params);
        } else if (Objects.equals(XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode(), businessType)) {
            entities = customerWorkLogModel.findEntitys(params);
        } else if (Objects.equals(XbbRefTypeEnum.SALES_OPPORTUNITY.getCode(), businessType)) {
            entities = opportunityWorkLogModel.findEntitys(params);
        } else if (Objects.equals(XbbRefTypeEnum.CLUE.getCode(), businessType)) {
            entities = clueWorkLogModel.findEntitys(params);
        }
        if (entities == null) {
            throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403003, MirrorErrorCodeEnum.API_ERROR_403003.getMsg(), I18nMessageUtil.getMessage(MessageConstant.STAGE_WORK_LOG) + "stageWorkLog：" + entities);
        } else if (entities.size() > 0) {
            List<Long> stageWorkLogIdList = new ArrayList<>();
            for (Object en : entities) {
                StageWorkLogEntity entity = (StageWorkLogEntity) en;
                stageWorkLogIdList.add(entity.getId());
            }
            String str = businessName + "，需要删除的阶段任务日志数据Id：" + JSON.toJSONString(stageWorkLogIdList);
            LOG.warn("镜像阶段推进器-" + "业务名：" + str);
            throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403051, MirrorErrorCodeEnum.API_ERROR_403051.getMsg(), I18nMessageUtil.getMessage(MessageConstant.STAGE_WORK_LOG), str);
        }
    }


    /**
     * 检查阶段日志是否有数据
     * @param formList
     * @param targetCorpid
     * @throws XbbException
     */
    private void checkStageLog(List<PaasFormEntity> formList, String targetCorpid) throws XbbException {
        //stage-log 4 张
        List<Long> customerFormIdIn = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        List<Long> clueFormIdIn = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        List<Long> opportunityFormIdIn = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        List<Long> paasFormIdIn = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (PaasFormEntity entity : formList) {
            if (Objects.equals(entity.getSaasMark(), SaasMarkEnum.PAAS.getCode())) {
                paasFormIdIn.add(entity.getId());
            } else if (Objects.equals(entity.getBusinessType(), XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode())) {
                customerFormIdIn.add(entity.getId());
            } else if (Objects.equals(entity.getBusinessType(), XbbRefTypeEnum.SALES_OPPORTUNITY.getCode())) {
                opportunityFormIdIn.add(entity.getId());
            } else if (Objects.equals(entity.getBusinessType(), XbbRefTypeEnum.CLUE.getCode())) {
                clueFormIdIn.add(entity.getId());
            }
        }

//        checkSingleStageLog(targetCorpid, paasFormIdIn, SaasMarkEnum.PAAS.getCode(), 0);
        checkSingleStageLog(targetCorpid, customerFormIdIn, SaasMarkEnum.SAAS.getCode(), XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode(), XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getName());
        checkSingleStageLog(targetCorpid, opportunityFormIdIn, SaasMarkEnum.SAAS.getCode(), XbbRefTypeEnum.SALES_OPPORTUNITY.getCode(), XbbRefTypeEnum.SALES_OPPORTUNITY.getName());
        checkSingleStageLog(targetCorpid, clueFormIdIn, SaasMarkEnum.SAAS.getCode(), XbbRefTypeEnum.CLUE.getCode(), XbbRefTypeEnum.CLUE.getName());

    }

    /**
     * 检查相关单据业务阶段日志是否有数据
     * @param corpid
     * @param FormIdIn
     * @param saas
     * @param businessType
     * @throws XbbException
     */
    private void checkSingleStageLog(String corpid, List<Long> FormIdIn, Integer saas, Integer businessType, String businessName) throws XbbException {
        if (FormIdIn.isEmpty()) {
            return;
        }
        Map<String, Object> params = new HashMap<>();
        params.put("corpid", corpid);
        params.put("formIdIn", FormIdIn);
        params.put("del", DelEnum.NORMAL.getDel());

        StageLogHandle stageLogHandle = stageLogHandleFactory.getStageLogHandle(saas, businessType);
        List<StageLogEntity> entitys = stageLogHandle.findEntitys(params);
        if (entitys.size() > 0) {
            List<Long> stageLogIdIn = new ArrayList<>();
            entitys.forEach(item -> {
                stageLogIdIn.add(item.getId());
            });
            String str = businessName + ",需要删除的阶段日志数据Id：" + JSON.toJSONString(stageLogIdIn);
            LOG.warn("镜像阶段推进器-" + "业务名：" + str);
            throw new XbbException(MirrorErrorCodeEnum.API_ERROR_403051, MirrorErrorCodeEnum.API_ERROR_403051.getMsg(), I18nMessageUtil.getMessage(MessageConstant.STAGE_LOG), str);
        }

    }

    /**
     * 更新以及删除表单解释
     * @param corpid
     * @param appid
     * @param retainAppId
     * @param systemFormIdIn
     * @throws XbbException
     */
    private void updateAndDeleteFormExplain(String corpid, Long appid, Long retainAppId, List<Long> systemFormIdIn) throws  XbbException {
        try {
            HashMap<String,Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("corpid", corpid);
            param.put("retainAppId", retainAppId);
            param.put("del",DelEnum.NORMAL.getDel());
            List<PaasFormExplainRetainEntity> entitys = paasFormExplainRetainModel.findEntity(param);
            HashMap<Long,PaasFormExplainRetainEntity> retainFormExplainMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            entitys.forEach(item -> retainFormExplainMap.put(item.getSourceFormExplainId(), item));

            param.clear();
            param.put("appId",appid);
            param.put("corpid",corpid);
            param.put("formIdIn", systemFormIdIn);
            List<PaasFormExplainEntity> paasFormExplainEntityList = paasFormExplainModel.findEntitys(param);
            paasFormExplainEntityList.forEach(item->{
                //要覆盖的修改
                PaasFormExplainRetainEntity paasFormExplainRetainEntity = retainFormExplainMap.get(item.getId());
                if (Objects.nonNull(paasFormExplainRetainEntity)) {
                    item.setExplains(paasFormExplainRetainEntity.getExplains());
                    item.setEnable(paasFormExplainRetainEntity.getEnable());
                    item.setDel(paasFormExplainRetainEntity.getDel());
                    item.setCreatorId(paasFormExplainRetainEntity.getCreatorId());
                    item.setUpdateTime(paasFormExplainRetainEntity.getUpdateTime());
                    item.setAddTime(paasFormExplainRetainEntity.getAddTime());
                } else {
                    item.setDel(DelEnum.DELETE.getDel());
                }
            });
            if (!paasFormExplainEntityList.isEmpty()) {
                paasFormExplainModel.updateBatch(paasFormExplainEntityList,corpid);
            }
        } catch (Exception e) {
            LOG.error("表单解释更新出错");
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
    }

    /**
     * 回退表单
     * @param corpid
     * @param appId
     * @param retainAppId
     * @param systemMenuIdIn
     * @throws XbbException
     * @return systemFormIds 返回所有系统表单id, 不管是要覆盖的还是要删除的
     */
    private List<Long> updateAndDeleteForm(String corpid, Long appId, Long retainAppId, List<Long> systemMenuIdIn, Map<Long,Long> sourceFormId2NowFormIdMap) throws XbbException {
        List<Long> systemFormIds = new ArrayList<>();
        try {
            HashMap<String,Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("corpid",corpid);
            param.put("retainAppId",retainAppId);
            param.put("del",DelEnum.NORMAL.getDel());
            List<PaasFormRetainEnity> paasFormRetainEnityList = paasFormRetainModel.findEntitys(param);
            HashMap<Long, PaasFormRetainEnity> retainFormMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            paasFormRetainEnityList.forEach(item-> retainFormMap.put(item.getSourceFormId(), item));

            param.clear();
            param.put("appId", appId);
            param.put("corpid", corpid);
            param.put("menuIdIn", systemMenuIdIn);
            List<PaasFormEntity> paasFormModelEntitys = paasFormModel.findEntity(param);
            for (PaasFormEntity entity : paasFormModelEntitys) {
                systemFormIds.add(entity.getId());
            }

            paasFormModelEntitys.forEach(item->{
                PaasFormRetainEnity paasFormRetainEnity = retainFormMap.get(item.getId());
                if (Objects.nonNull(paasFormRetainEnity)) {
                    sourceFormId2NowFormIdMap.put(paasFormRetainEnity.getSourceFormId(),item.getId());
                    item.setDel(paasFormRetainEnity.getDel());
                    item.setEnable(paasFormRetainEnity.getEnable());
                    item.setAssociationList(paasFormRetainEnity.getAssociationList());
                    item.setSummary(paasFormRetainEnity.getSummary());
                    item.setUpdateTime(paasFormRetainEnity.getUpdateTime());
                    item.setName(paasFormRetainEnity.getName());
                    item.setBusinessRules(paasFormRetainEnity.getBusinessRules());
                    item.setCustomTitle(paasFormRetainEnity.getCustomTitle());
                    item.setLabels(paasFormRetainEnity.getLabels());
                    item.setCoOpPerms(paasFormRetainEnity.getCoOpPerms());
                    item.setCreatorId(paasFormRetainEnity.getCreatorId());
                    item.setSurcharge(paasFormRetainEnity.getSurcharge());
                    item.setIsProcessForm(paasFormRetainEnity.getIsProcessForm());
                    item.setDisableFieldAssign(paasFormRetainEnity.getDisableFieldAssign());
                    item.setAddTime(paasFormRetainEnity.getAddTime());
                    item.setFrontCache(paasFormRetainEnity.getFrontCache());
                    item.setOpenDistribution(paasFormRetainEnity.getOpenDistribution());
                    item.setPermission(paasFormRetainEnity.getPermission());
                    item.setPriceAssociation(paasFormRetainEnity.getPriceAssociation());
                    item.setTitleType(paasFormRetainEnity.getTitleType());
                    item.setVerifyRule(paasFormRetainEnity.getVerifyRule());
                    item.setDingtalkProcessCode(paasFormRetainEnity.getDingtalkProcessCode());
                } else {
                    item.setDel(DelEnum.DELETE.getDel());
                }
            });
            paasFormModel.updateBatch(paasFormModelEntitys, corpid);
        } catch (Exception e) {
            LOG.error("回退到上一版本 表单覆盖出错");
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        return systemFormIds;
    }

    /**
     * 更新并删除（覆盖回退）阶段推进器
     * @param stageRollbackPojo
     * @throws XbbException
     */
    private void updateAndDeleteStageThruster(StageRollbackPojo stageRollbackPojo) throws XbbException {
        try {
            rollBackStageProcess(stageRollbackPojo);

            rollBackStage(stageRollbackPojo);

            rollBackStageWork(stageRollbackPojo);

        } catch (Exception e) {
            LOG.error("回退到上一版本 阶段推进器覆盖出错", e.getMessage());
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
    }

    /**
     * 回退阶段流程
     * @param stageRollbackPojo
     * @throws XbbException
     */
    private void rollBackStageProcess(StageRollbackPojo stageRollbackPojo) throws XbbException {
        Map<Long, Long> sourceStageProcessId2NowMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        stageRollbackPojo.setSourceStageProcessId2NowMap(sourceStageProcessId2NowMap);
        String corpid = stageRollbackPojo.getCorpid();
        Long retainAppId = stageRollbackPojo.getRetainAppId();
        List<Long> retainFormIdIn = stageRollbackPojo.getRetainFormIdIn();
        Map<Long, Long> sourceFormId2NowFormIdMap = stageRollbackPojo.getSourceFormId2NowFormIdMap();

        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("corpid", corpid);
        param.put("retainAppId", retainAppId);
        param.put("retainFormIdIn", retainFormIdIn);
        param.put("del", DelEnum.NORMAL.getDel());
        List<StageProcessRetainEntity> stageProcessRetainEntityList = stageProcessRetainModel.findEntitys(param);

        param.clear();
        param.put("corpid", corpid);
        param.put("formIdIn", retainFormIdIn);
        List<StageProcessEntity> stageProcessNowEntityList = stageProcessModel.findEntitys(param);

        int index = 0;
        for (StageProcessEntity entity : stageProcessNowEntityList) {
            if (index < stageProcessRetainEntityList.size()) {
                StageProcessRetainEntity retainEntity = stageProcessRetainEntityList.get(index);
                sourceStageProcessId2NowMap.put(retainEntity.getRetainStageProcessId(),entity.getId());

                entity.setFormId(sourceFormId2NowFormIdMap.get(retainEntity.getRetainFormId()));
                entity.setName(retainEntity.getName());
                entity.setVersionName(retainEntity.getVersionName());
                entity.setVersionId(retainEntity.getVersionId());
                entity.setEnable(retainEntity.getEnable());
                entity.setStageUnit(retainEntity.getStageUnit());
                entity.setAllowBack(retainEntity.getAllowBack());
                entity.setAllowNewSelect(retainEntity.getAllowNewSelect());
                entity.setAllowNewSelectUser(retainEntity.getAllowNewSelectUser());
                entity.setShowTimeUse(retainEntity.getShowTimeUse());
                entity.setShowTimeStay(retainEntity.getShowTimeStay());
                entity.setShowWorkCompletion(retainEntity.getShowWorkCompletion());
                entity.setShowAverageCompletionTime(retainEntity.getShowAverageCompletionTime());
                entity.setTriggerCondition(retainEntity.getTriggerCondition());
                entity.setIsArchive(retainEntity.getIsArchive());
                entity.setCreatorId(retainEntity.getCreatorId());
                entity.setOldTransferVersion(retainEntity.getOldTransferVersion());
                entity.setAddTime(retainEntity.getAddTime());
                entity.setUpdateTime(retainEntity.getUpdateTime());
                entity.setDel(retainEntity.getDel());
            } else {
                entity.setDel(DelEnum.DELETE.getDel());
            }
            ++index;
        }
        if (!stageProcessNowEntityList.isEmpty()) {
            stageProcessModel.updateBatch4MirrorRollback(stageProcessNowEntityList, corpid);
        }
    }


    /**
     * 回退阶段
     * @param stageRollbackPojo
     * @throws XbbException
     */
    private void rollBackStage(StageRollbackPojo stageRollbackPojo) throws XbbException {
        Map<Long, Long> sourceStageId2NowMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        stageRollbackPojo.setSourceStageId2NowMap(sourceStageId2NowMap);
        String corpid = stageRollbackPojo.getCorpid();
        Long retainAppId = stageRollbackPojo.getRetainAppId();
        List<Long> retainFormIdIn = stageRollbackPojo.getRetainFormIdIn();

        // Stage
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("corpid", corpid);
        param.put("retainAppId", retainAppId);
        param.put("retainFormIdIn", retainFormIdIn);
        param.put("del", DelEnum.NORMAL.getDel());
        List<StageRetainEntity> stageRetainEntityList = stageRetainModel.findEntitys(param);


        List<StageRetainEntity> paasStageRetainEntityList = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        List<StageRetainEntity> customerStageRetainEntityList = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        List<StageRetainEntity> clueStageRetainEntityList = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        List<StageRetainEntity> opportunityStageRetainEntityList = new ArrayList<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);

        //要将这个 列表 分成四组，插进四张表中。
        for (StageRetainEntity entity : stageRetainEntityList) {
            if (Objects.equals(entity.getRetainSaasMark(), SaasMarkEnum.SAAS.getCode())) {
                if (Objects.equals(entity.getRetainBusinessType(), XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode())) {
                    customerStageRetainEntityList.add(entity);
                } else if (Objects.equals(entity.getRetainBusinessType(), XbbRefTypeEnum.CLUE.getCode())) {
                    clueStageRetainEntityList.add(entity);
                } else if (Objects.equals(entity.getRetainBusinessType(), XbbRefTypeEnum.SALES_OPPORTUNITY.getCode())) {
                    opportunityStageRetainEntityList.add(entity);
                }
            } else if (Objects.equals(entity.getRetainSaasMark(), SaasMarkEnum.PAAS.getCode())) {
                paasStageRetainEntityList.add(entity);
            }
        }

        rollBack4SingleStage(stageRollbackPojo, paasStageRetainEntityList, SaasMarkEnum.PAAS.getCode(), 0);
        rollBack4SingleStage(stageRollbackPojo, customerStageRetainEntityList, SaasMarkEnum.SAAS.getCode(), XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode());
        rollBack4SingleStage(stageRollbackPojo, clueStageRetainEntityList, SaasMarkEnum.SAAS.getCode(), XbbRefTypeEnum.CLUE.getCode());
        rollBack4SingleStage(stageRollbackPojo, opportunityStageRetainEntityList, SaasMarkEnum.SAAS.getCode(), XbbRefTypeEnum.SALES_OPPORTUNITY.getCode());
    }

    /**
     * 为单个阶段回退
     * @param stageRollbackPojo
     * @param stageRetainEntityList
     * @param saasMark
     * @param businessType
     * @throws XbbException
     */
    private void rollBack4SingleStage(StageRollbackPojo stageRollbackPojo, List<StageRetainEntity> stageRetainEntityList, Integer saasMark, Integer businessType) throws XbbException {

        String corpid = stageRollbackPojo.getCorpid();
        List<Long> formIdIn = stageRollbackPojo.getRetainFormIdIn();
        Map<Long, Long> sourceFormId2NowFormIdMap = stageRollbackPojo.getSourceFormId2NowFormIdMap();
        Map<Long, Long> sourceStageProcessId2NowMap = stageRollbackPojo.getSourceStageProcessId2NowMap();
        Map<Long,Long> sourceStageId2NowMap = stageRollbackPojo.getSourceStageId2NowMap();

        HashMap<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("corpid", corpid);
        param.put("formIdIn", formIdIn);
        StageHandle stageHandle = stageHandleFactory.getStageHandle(saasMark, businessType);
        List<PaasStageEntity> stageEntityNowList = stageHandle.findEntitys(param);

        int index = 0;
        for (PaasStageEntity entity : stageEntityNowList) {
            if (index < stageRetainEntityList.size()) {
                StageRetainEntity retainEntity = stageRetainEntityList.get(index);
                sourceStageId2NowMap.put(retainEntity.getRetainStageId(),entity.getId());

                entity.setFormId(sourceFormId2NowFormIdMap.get(retainEntity.getRetainFormId()));
                entity.setStageProcessId(sourceStageProcessId2NowMap.get(retainEntity.getRetainStageProcessId()));
                entity.setName(retainEntity.getName());
                entity.setCode(retainEntity.getCode());
                entity.setIntoProportion(retainEntity.getIntoProportion());
                entity.setOutProportion(retainEntity.getOutProportion());
                entity.setType(retainEntity.getType());
                entity.setEnable(retainEntity.getEnable());
                entity.setSort(retainEntity.getSort());
                entity.setStayLimit(retainEntity.getStayLimit());
                entity.setStayLimitTime(retainEntity.getStayLimitTime());
                entity.setStayLimitTimeUnit(retainEntity.getStayLimitTimeUnit());
                entity.setEnableStageWarningColor(retainEntity.getEnableStageWarningColor());
                entity.setStageWarningColor(retainEntity.getStageWarningColor());
                entity.setExecutor(retainEntity.getExecutor());
                entity.setPreConditions(retainEntity.getPreConditions());
                entity.setAllowSkipStage(retainEntity.getAllowSkipStage());
                entity.setOpenStatus(retainEntity.getOpenStatus());
                entity.setBusinessRelatedStatus(retainEntity.getBusinessRelatedStatus());
                entity.setExtraSetting(retainEntity.getExtraSetting());
                entity.setAllowReactive(retainEntity.getAllowReactive());
                entity.setAllowReactiveStage(retainEntity.getAllowReactiveStage());
                entity.setEnableAutoJump(retainEntity.getEnableAutoJump());
                entity.setEnableJumpEnd(retainEntity.getEnableJumpEnd());
                entity.setIsDefault(retainEntity.getIsDefault());
                entity.setLinkOption(retainEntity.getLinkOption());
                entity.setAddTime(retainEntity.getAddTime());
                entity.setUpdateTime(retainEntity.getUpdateTime());
                entity.setDel(retainEntity.getDel());
                entity.setAllowCreateContractSkipStage(retainEntity.getAllowCreateContractSkipStage());
            } else {
                entity.setDel(DelEnum.DELETE.getDel());
            }
            ++index;
        }
        if (!stageEntityNowList.isEmpty()) {
            stageHandle.updateBatch4MirrorRollback(stageEntityNowList, corpid);
        }
    }

    /**
     * 回退阶段工作
     * @param stageRollbackPojo
     * @throws XbbException
     */
    private void rollBackStageWork(StageRollbackPojo stageRollbackPojo) throws XbbException {
        String corpid = stageRollbackPojo.getCorpid();
        Long retainAppId = stageRollbackPojo.getRetainAppId();
        List<Long> formIdIn = stageRollbackPojo.getRetainFormIdIn();
        Map<Long, Long> sourceFormId2NowFormIdMap = stageRollbackPojo.getSourceFormId2NowFormIdMap();
        Map<Long, Long> sourceStageProcessId2NowMap = stageRollbackPojo.getSourceStageProcessId2NowMap();
        Map<Long, Long> sourceStageId2NowMap = stageRollbackPojo.getSourceStageId2NowMap();

        HashMap<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("corpid", corpid);
        param.put("retainAppId", retainAppId);
        param.put("retainFormIdIn", formIdIn);
        param.put("del", DelEnum.NORMAL.getDel());
        List<StageWorkRetainEntity> stageWorkRetainEntityList = stageWorkRetainModel.findEntitys(param);

        param.clear();
        param.put("corpid", corpid);
        param.put("formIdIn", formIdIn);
        List<StageWorkEntity> stageWorkNowEntityList = stageWorkModel.findEntitys(param);

        int index = 0;
        for (StageWorkEntity entity : stageWorkNowEntityList) {
            if (index < stageWorkRetainEntityList.size()) {
                StageWorkRetainEntity retainEntity = stageWorkRetainEntityList.get(index);
                entity.setFormId(sourceFormId2NowFormIdMap.get(retainEntity.getRetainFormId()));
                entity.setStageProcessId(sourceStageProcessId2NowMap.get(retainEntity.getRetainStageProcessId()));
                entity.setStageId(sourceStageId2NowMap.get(retainEntity.getRetainStageId()));
                entity.setStageCode(retainEntity.getStageCode());
                entity.setBusinessType(retainEntity.getBusinessType());
                entity.setSaasMark(retainEntity.getSaasMark());
                entity.setName(retainEntity.getName());
                entity.setDescription(retainEntity.getDescription());
                entity.setProportion(retainEntity.getProportion());
                entity.setRequired(retainEntity.getRequired());
                entity.setExecutionEvent(retainEntity.getExecutionEvent());
                entity.setExecutor(retainEntity.getExecutor());
                entity.setCompletionCondition(retainEntity.getCompletionCondition());
                entity.setSort(retainEntity.getSort());
                entity.setDel(retainEntity.getDel());
                entity.setAddTime(retainEntity.getAddTime());
                entity.setUpdateTime(retainEntity.getUpdateTime());
            } else {
                entity.setDel(DelEnum.DELETE.getDel());
            }
            ++index;
        }
        if (!stageWorkNowEntityList.isEmpty()) {
            stageWorkModel.updateBatch4MirrorRollback(stageWorkNowEntityList, corpid);
        }
    }

    /**
     * 回退工单表单解释
     * @param corpid
     * @param appId
     * @param retainAppId
     * @param formIds 工单表单
     */
    private void updateAndDeleteWorkOrderFormExplain(String corpid, Long appId, Long retainAppId, List<Long> formIds) {
        HashMap<String,Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("corpid",corpid);
        param.put("retainAppId", retainAppId);
        param.put("del",DelEnum.NORMAL.getDel());
        List<PaasFormExplainRetainEntity> paasFormExplainRetainEntityList = paasFormExplainRetainModel.findEntity(param);
        HashMap<Long,PaasFormExplainRetainEntity> retainFormExplainMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        paasFormExplainRetainEntityList.forEach(item -> retainFormExplainMap.put(item.getSourceFormExplainId(), item));

        param.clear();
        param.put("corpid",corpid);
        param.put("appId", appId);
        param.put("formIdIn", formIds);
        List<WorkOrderExplainEntity> workOrderExplainEntityList = workOrderExplainModel.findEntitys(param);

        workOrderExplainEntityList.forEach(item->{
            PaasFormExplainRetainEntity paasFormExplainRetainEntity = retainFormExplainMap.get(item.getId());
            if (Objects.nonNull(paasFormExplainRetainEntity)) {
                item.setAddTime(paasFormExplainRetainEntity.getAddTime());
                item.setDel(paasFormExplainRetainEntity.getDel());
                item.setCreatorId(paasFormExplainRetainEntity.getCreatorId());
                item.setExplains(paasFormExplainRetainEntity.getExplains());
                item.setUpdateTime(paasFormExplainRetainEntity.getUpdateTime());
            }else{
                item.setDel(DelEnum.DELETE.getDel());
            }
        });
        if (!workOrderExplainEntityList.isEmpty()) {
            workOrderExplainModel.updateBatch(workOrderExplainEntityList, corpid);
        }
    }

    /**
     * 回退工单表单
     * @param corpid
     * @param appId
     * @param retainAppId
     * @param systemTemplateIdIn
     * @return
     */
    private List<Long> updateAndDeleteWorkOrderForm(String corpid, Long appId, Long retainAppId, List<Long> systemTemplateIdIn) {
        HashMap<String ,Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("corpid", corpid);
        param.put("appId", appId);
        param.put("templateIdIn", systemTemplateIdIn);
        param.put("type", FormBindTemplateTypeEnum.BACK.getType());
        param.put("del",DelEnum.NORMAL.getDel());
        List<FormBindTemplateInfoEntity> formBindInfoList = formBindTemplateInfoModel.findEntitys(param);

        List<Long> formIds = new ArrayList<>();
        HashMap<Long, Long> bindFormMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        formBindInfoList.forEach(item -> {
            // formIds.add(item.getFormId());
            bindFormMap.put(item.getFormId(), item.getTemplateId());
        });

        param.clear();
        param.put("corpid", corpid);
        param.put("retainAppId", retainAppId);
        // param.put("sourceFormIdIn", formIds);
        param.put("del", DelEnum.NORMAL.getDel());
        List<PaasFormRetainEnity> paasFormRetainEnityList = paasFormRetainModel.findEntitys(param);
        HashMap<Long,PaasFormRetainEnity> retainFormMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        paasFormRetainEnityList.forEach(item ->{
            retainFormMap.put(item.getSourceFormId(),item);
            formIds.add(item.getSourceFormId());
        });

        param.clear();
        param.put("corpid",corpid);
        param.put("appId", appId);
        param.put("templateIdIn", systemTemplateIdIn);
        param.put("formIds",formIds);
        List<WorkOrderFormEntity> workOrderFormEntityList = workOrderFormModel.findEntitys(param);
        for (WorkOrderFormEntity workOrderFormEntity : workOrderFormEntityList) {
            PaasFormRetainEnity paasFormRetainEnity = retainFormMap.get(workOrderFormEntity.getId());
            if (Objects.nonNull(paasFormRetainEnity)){
                workOrderFormEntity.setDel(paasFormRetainEnity.getDel());
                workOrderFormEntity.setAddTime(paasFormRetainEnity.getAddTime());
                workOrderFormEntity.setAssociationList(paasFormRetainEnity.getAssociationList());
                workOrderFormEntity.setBusinessRules(paasFormRetainEnity.getBusinessRules());
                workOrderFormEntity.setCoOpPerms(paasFormRetainEnity.getCoOpPerms());
                workOrderFormEntity.setCreatorId(paasFormRetainEnity.getCreatorId());
                workOrderFormEntity.setCustomTitle(paasFormRetainEnity.getCustomTitle());
                workOrderFormEntity.setDisableFieldAssign(paasFormRetainEnity.getDisableFieldAssign());
                workOrderFormEntity.setEnable(paasFormRetainEnity.getEnable());
                workOrderFormEntity.setFrontCache(paasFormRetainEnity.getFrontCache());
                workOrderFormEntity.setIsProcessForm(paasFormRetainEnity.getIsProcessForm());
                workOrderFormEntity.setLabels(paasFormRetainEnity.getLabels());
                workOrderFormEntity.setName(paasFormRetainEnity.getName());
                workOrderFormEntity.setSummary(paasFormRetainEnity.getSummary());
                workOrderFormEntity.setTemplateId(bindFormMap.get(paasFormRetainEnity.getSourceFormId()));
                workOrderFormEntity.setTitleType(paasFormRetainEnity.getTitleType());
                workOrderFormEntity.setVerifyRule(paasFormRetainEnity.getVerifyRule());
            }else{
                workOrderFormEntity.setDel(DelEnum.DELETE.getDel());
            }
        }
        if (!workOrderFormEntityList.isEmpty()) {
            workOrderFormModel.updateBatch(workOrderFormEntityList,corpid);
        }

        return formIds;
    }

    /**
     * 回退工单流程
     * @param corpid
     * @param appId
     * @param retainAppId
     * @param systemTemplateIdIn 默认分类下的工单模板
     */
    private void updateAndDeleteWorkOrderStage(String corpid, Long appId, Long retainAppId, List<Long> systemTemplateIdIn) {
        HashMap<String ,Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("corpid",corpid);
        param.put("templateIdIn", systemTemplateIdIn);
        List<WorkOrderStageEntity> workOrderStageEntityList = workOrderStageModel.findEntitys(param);

        param.clear();
        param.put("corpid", corpid);
        param.put("retainAppId", retainAppId);
        param.put("del",DelEnum.NORMAL.getDel());
        List<WorkOrderStageRetainEntity> retainStageEntityList = workOrderStageRetainModel.findEntitys(param);
        HashMap<Long,WorkOrderStageRetainEntity> retainStageMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        retainStageEntityList.forEach(item-> retainStageMap.put(item.getSourceStageId(),item));

        workOrderStageEntityList.forEach(item->{
            WorkOrderStageRetainEntity workOrderStageRetainEntity = retainStageMap.get(item.getId());
            if (Objects.nonNull(workOrderStageRetainEntity)) {
                item.setUpdateTime(workOrderStageRetainEntity.getUpdateTime());
                item.setAddTime(workOrderStageRetainEntity.getAddTime());
                item.setDel(workOrderStageRetainEntity.getDel());
                item.setExpectedTime(workOrderStageRetainEntity.getExpectedTime());
                item.setIsLast(workOrderStageRetainEntity.getIsLast());
                item.setName(workOrderStageRetainEntity.getName());
                item.setRefRobId(workOrderStageRetainEntity.getRefRobId());
                item.setRefRobName(workOrderStageRetainEntity.getRefRobName());
                item.setRefRobType(workOrderStageRetainEntity.getRefRobType());
                item.setRuleId(workOrderStageRetainEntity.getRuleId());
                item.setSort(workOrderStageRetainEntity.getSort());
                item.setUserAvatar(workOrderStageRetainEntity.getUserAvatar());
            }else{
                item.setDel(DelEnum.DELETE.getDel());
            }
        });
        if (!workOrderStageEntityList.isEmpty()) {
            workOrderStageModel.updateBatch(workOrderStageEntityList, corpid);
        }
    }

    /**
     * 回退工单模板
     * @param corpid
     * @param appId
     * @param retainAppId 备份应用id
     * @param defaultCategoryId 默认工单分类
     * @return systemTemplateIds 默认分类下的工单模板，不管是删除还是没删除
     */
    private List<Long> updateAndDeleteWorkOrderTemplate(String corpid, Long appId, Long retainAppId, Long defaultCategoryId) {
        HashMap<String,Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("corpid", corpid);
        param.put("appId", appId);
        param.put("categoryId", defaultCategoryId);
        List<WorkOrderTemplateEntity> workOrderTemplates = workOrderTemplateModel.findEntity(param);

        param.clear();
        param.put("corpid", corpid);
        param.put("retainAppId", retainAppId);
        param.put("del",DelEnum.NORMAL.getDel());
        List<WorkOrderTemplateRetainEntity> workOrderTemplateRetainEntities = workOrderTemplateRetainModel.findEntitys(param);
        HashMap<Long,WorkOrderTemplateRetainEntity> retainTemplateMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        workOrderTemplateRetainEntities.forEach(item-> retainTemplateMap.put(item.getSourceTemplateId(), item));

        List<Long> systemTemplateIds = new ArrayList<>();
        workOrderTemplates.forEach(item -> {
            WorkOrderTemplateRetainEntity workOrderTemplateRetainEntity = retainTemplateMap.get(item.getId());
            if (Objects.nonNull(workOrderTemplateRetainEntity)) {
                item.setEnable(workOrderTemplateRetainEntity.getEnable());
                item.setDel(workOrderTemplateRetainEntity.getDel());
                item.setExpectedTime(workOrderTemplateRetainEntity.getExpectedTime());
                item.setIsFree(workOrderTemplateRetainEntity.getIsFree());
                item.setName(workOrderTemplateRetainEntity.getName());
                item.setCc(workOrderTemplateRetainEntity.getCc());
                item.setCorpid(workOrderTemplateRetainEntity.getCorpid());
                item.setCreatorName(workOrderTemplateRetainEntity.getCreatorName());
                item.setIntroduction(workOrderTemplateRetainEntity.getIntroduction());
                item.setModifierId(workOrderTemplateRetainEntity.getModifierId());
                item.setModifierName(workOrderTemplateRetainEntity.getModifierName());
                item.setCategoryId(workOrderTemplateRetainEntity.getCategoryId());
                item.setScopeRule(workOrderTemplateRetainEntity.getScopeRule());
                item.setAddTime(workOrderTemplateRetainEntity.getAddTime());
            }else{
                item.setDel(DelEnum.DELETE.getDel());
            }
            systemTemplateIds.add(item.getId());
        });
        if (!workOrderTemplates.isEmpty()) {
            workOrderTemplateModel.updateBatch(workOrderTemplates, corpid);
        }
        return systemTemplateIds;
    }

    /**
     * 工单分类回退到上一版本
     * @param corpid
     * @param appid
     * @param retainAppId 备份应用id
     * @return defaultCategoryId 默认工单分类
     */
    private Long updateAndDeleteWorkOrderCategory(String corpid, Long appid, Long retainAppId) throws XbbException {
        Long defaultCategoryId = 0L;
        // 查询系统默认分类(默认分类只有一个，一对一匹配就行)
        HashMap<String,Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("corpid",corpid);
        param.put("appId",appid);
        param.put("isDefault", 1);
        List<WorkOrderTemplateCategoryEntity> categoryEntityList = workOrderTemplateCategoryModel.findEntitys(param);
        if (!categoryEntityList.isEmpty()) {
            WorkOrderTemplateCategoryEntity categoryEntity = categoryEntityList.get(0);
            WorkOrderTemplateCategoryRetainEntity retainCategoryEntity = workOrderTemplateCategoryRetainModel.getCategoryByRetainAppId(retainAppId, corpid);
            categoryEntity.setName(retainCategoryEntity.getName());
            categoryEntity.setSort(retainCategoryEntity.getSort());
            categoryEntity.setUpdateTime(retainCategoryEntity.getUpdateTime());
            categoryEntity.setCreatorId(retainCategoryEntity.getCreatorId());
            categoryEntity.setModifierId(retainCategoryEntity.getModifierId());
            categoryEntity.setIsDefault(retainCategoryEntity.getIsDefault());
            categoryEntity.setModifierName(retainCategoryEntity.getModifierName());
            workOrderTemplateCategoryModel.update(categoryEntity);
            defaultCategoryId = categoryEntity.getId();
        }
        return defaultCategoryId;
    }

    private void updateAndDeletePrintTemplate(String corpid, Long appid, Long retainAppId, List<Long> systemFormIdIn)  throws XbbException{
        try {
            HashMap<String,Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("retainAppId",retainAppId);
            param.put("corpid",corpid);
            param.put("del",DelEnum.NORMAL.getDel());
            List<PaasPrintTemplateRetainEntity> entitys = paasPrintTemplateRetainModel.list(param);
            HashMap<Long,PaasPrintTemplateRetainEntity> retainPrintTemplateMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            entitys.forEach(item-> retainPrintTemplateMap.put(item.getSourcePrintTemplateId(), item));

            param.clear();
            param.put("appId",appid);
            param.put("corpid",corpid);
            param.put("formIdIn", systemFormIdIn);
            List<PaasPrintTemplateEntity> paasPrintTemplateEntityList = paasPrintTemplateDao.findEntitys(param);
            paasPrintTemplateEntityList.forEach(item->{
                PaasPrintTemplateRetainEntity paasPrintTemplateRetainEntity = retainPrintTemplateMap.get(item.getId());
                if (Objects.nonNull(paasPrintTemplateRetainEntity)) {
                    item.setUpdateTime(paasPrintTemplateRetainEntity.getUpdateTime());
                    item.setCreatorId(paasPrintTemplateRetainEntity.getCreatorId());
                    item.setConfigInfo(paasPrintTemplateRetainEntity.getConfigInfo());
                    item.setHtml(paasPrintTemplateRetainEntity.getHtml());
                    item.setName(paasPrintTemplateRetainEntity.getName());
                    item.setDel(paasPrintTemplateRetainEntity.getDel());
                    item.setEnable(paasPrintTemplateRetainEntity.getEnable());
                } else {
                    item.setDel(DelEnum.DELETE.getDel());
                }
            });
            if (!paasPrintTemplateEntityList.isEmpty()) {
                paasPrintTemplateDao.updateBatch(paasPrintTemplateEntityList,corpid);
            }
        }  catch (Exception e){
            LOG.error("覆盖打印模板出错", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }

    }

    /**
     * 回退菜单
     * 注：系统子菜单目前不能增加和删除，应该就一对一回退就行了; 以后子菜单要是有增加删除操作那就要通过父菜单查询子菜单做一对一比较
     * @param corpid
     * @param appId 应用id
     * @param retainAppId 应用备份id
     * @return
     * @throws XbbException
     */
    private List<Long> updateAndDeleteMenu(String corpid, Long appId, Long retainAppId) throws XbbException {
        List<Long> systemMenuIdIn  = new ArrayList<>();
        try {
            HashMap<String,Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put("retainAppId",retainAppId);
            param.put("corpid",corpid);
            param.put("del",DelEnum.NORMAL.getDel());
            List<PaasMenuRetainEntity> paasMenuRetainEntities = paasMenuRetainModel.findEntitys(param);
            List<Long> retainMenuIdIn  = new ArrayList<>();
            paasMenuRetainEntities.forEach(item->{
                retainMenuIdIn.add(item.getSourceMenuId());
            });

            if (retainMenuIdIn.size() > 0) {
                //查询对应的的菜单数据
                param.clear();
                param.put("idIn", retainMenuIdIn);
                param.put("appId", appId);
                param.put("corpid",corpid);
                List<PaasMenuEntity> entitys = paasMenuModel.findEntitys(param);
                HashMap<Long, PaasMenuEntity> paasMenuMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                entitys.forEach(item-> paasMenuMap.put(item.getId(), item));
                List<PaasMenuEntity> paasMenuEntityList = new ArrayList<>();
                paasMenuRetainEntities.forEach(item->{
                    PaasMenuEntity paasMenuEntity = paasMenuMap.get(item.getSourceMenuId());
                    if (Objects.nonNull(paasMenuEntity)){
                        paasMenuEntity.setEnable(item.getEnable());
                        paasMenuEntity.setDel(item.getDel());
                        paasMenuEntity.setMobileUrl(item.getMobileUrl());
                        paasMenuEntity.setType(item.getType());
                        paasMenuEntity.setUrl(item.getUrl());
                        paasMenuEntity.setColor(item.getColor());
                        paasMenuEntity.setIcon(item.getIcon());
                        paasMenuEntity.setName(item.getName());
                        paasMenuEntity.setSort(item.getSort());
                        paasMenuEntity.setParentId(item.getParentId());
                        paasMenuEntity.setCreatorId(item.getCreatorId());
                        paasMenuEntity.setUpdateTime(item.getUpdateTime());
                        paasMenuEntityList.add(paasMenuEntity);
                        systemMenuIdIn.add(paasMenuEntity.getId());
                    }
                });
                paasMenuModel.updateBatch(paasMenuEntityList,corpid);
            }
        }  catch (Exception e){
            LOG.error("覆盖菜单出错", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
        return systemMenuIdIn;
    }

    private void row2setLevel(List<FieldAttrPojo> rowGroupAttrList, ChartStatisticsPojo rowChartPojo, int size) {
        FieldAttrPojo fieldAttrPojo = rowGroupAttrList.get(size);
        String labelName = fieldAttrPojo.getLabelName();
        String label = fieldAttrPojo.getLabel();
        String attrName = fieldAttrPojo.getAttrName();
        rowChartPojo.setKey(fieldAttrPojo.getAttr() + "&" + fieldAttrPojo.getFieldType());
        rowChartPojo.setValue(labelName);
        if (Objects.equals(labelName,label)) {
            if (FieldTypeEnum.SUB_FORM.getType().intValue() == fieldAttrPojo.getFieldType()) {
                rowChartPojo.setValue(attrName);
            } else {
                rowChartPojo.setValue(Arrays.asList(label.split("--")).get(0) + "--" + attrName);
            }
        }
        rowChartPojo.setHead(true);
        rowChartPojo.setFormId(fieldAttrPojo.getFormId());
        if (size < rowGroupAttrList.size() - 1) {
            ChartStatisticsPojo pojo = new ChartStatisticsPojo();
            List<ChartStatisticsPojo> rowChild = new ArrayList<>();
            size++;
            row2setLevel(rowGroupAttrList, pojo,size);
            rowChild.add(pojo);
            rowChartPojo.setChild(rowChild);
        }
        if (fieldAttrPojo.getDateGroupType() != null) {
            rowChartPojo.setDateGroupType(fieldAttrPojo.getDateGroupType());
        }
    }

}
